function ret = dsp_cgfcdb(action,varargin)
%DSP_CGFCDB DSP Code Generation Function Call Data Base
%   Manages the creation and removal of block instance data
%   in the function-call data base for DSP code generation.
%   Called from TLC code generation functions as required.
%
% See also DSP_CGFC, DSP_CGPREF.

% Author: D. Orofino
% Copyright 1995-2000 The MathWorks, Inc.
% $Revision: 1.4 $ $Date: 2000/05/05 20:16:21 $

%   Valid actions are:
%   'begin'   Record block instance to database.
%   'end'     Remove block instance from database.
%   'query'   Return entire database structure.
%   'reset'   Reset database; only used when errors occur.
%   'set'     Set a database entry
%
%   Example TLC calls:
%   1. Add block instance data to database
%      Called by blkutils.tlc:DSPInitialize()
%
%      %assign err = FEVAL("dsp_cgfcdb","begin",block)
%
%   2. Remove block instance data from database
%      Typically done at very end of TLC Outputs function
%
%      %assign err = FEVAL("dsp_cgfcdb","end",block)


% Setup and initialize database as a persistent memory object.
% The database is a structure with the following fields:
%
%         db: The database itself (a nested structure).
%  db_endcnt: A count of references to the database.
%
% Database tear-down must be deferred until the last reference
%   to the database has been released; therefore, a count of
%   the number of references must be maintained.
%
persistent db db_endcnt
if isempty(db_endcnt),
   [db,db_endcnt] = resetDatabase;
end

% Make sure action argument is present:
if nargin<1, error('Too few input arguments.'); end

% Take action:
%
switch lower(action)
   
case 'query'
   %
   % Return database contents:
   %
   ret = db;
   
case 'set'
   %
   % Set a database entry
   %
   db(varargin{1}) = varargin{2};
   ret = 0;
   
case 'reset'
   %
   % Remove existing database contents and re-initialize:
   %
   [db,db_endcnt] = resetDatabase;
   ret = 0;
   
   
case 'begin'
   %
   % Record instance data for block into database.
   % Creates database if it is not present.
   %
   % Call using:
   %   dsp_cgfcdb('begin',blk)
   % where:
   %   blk = TLC structure containing block instance data
   %
   % Creates the following database records:
   %  .Identifier   The block identifier from the TLC data
   %
   %  .SFcnName     A parameter named sfcnName is always generated and
   %                recorded in the database for each call to 'begin'.
   %                Thus, the following field is always present:
   %                      db.SFcnName = '<sfcnNameString>'
   %                where '<sfcnNameString>' is filled in as appropriate.
   %                If the block is not an S-Fcn, an error will result.
   %
   %  .SameParamsIdx - cache for the "sameparams" function call, so that
   %                   subsequent calls do not need to recompute this data.
   %                   This query is in dsp_cgfc (the query engine).
   %
   %  .SFcnPartition - caches the partition number for this block instance.
   %                   -1 = not computed, 0=not in a partition,
   %                   >=1 = partition #
   %          
%
   %
   %  .params       A structure of param/value pairs passed by the caller
   %                to be maintained in the database for this block.
   %                Each field of .params is one parameter name, and the
   %                corresponding value is recorded as the field content.

   % Get block record, and verify that it is an S-Function:
   %
   blk = varargin{1};
   error(checkSFcn(blk));
   
   % If the database is empty, it implies that we are just
   % beginning code generation for this model.  Return this
   % info to the caller, so that we can display an appropriate
   % message, etc.
   %
   ret = isempty(db);
   
   % New database structure entry to hold block info:
   %
   newEntry = [];

   % Add instance identifier which uniquely names this block instance:
   %
   newEntry.Identifier = blk.Identifier;
   
   % Add S-Function name to database
   %
   newEntry.SFcnName = blk.ParamSettings.FunctionName;
   
   % Add list of database indices to other blocks with
   % "same" parameters.  The default is -1 (illegal index).
   % This is updated in dsp_cgfc when needed:
   %
   newEntry.SameParamsIdx = -1;
   
   % Add default S-Fcn partition number
   %
   % One or more functions may be generated for a given S-Fcn,
   % and each generated function needs a unique name.
   %
   % The blocks with a certain set of parameters mapping to
   % a certain generated function are called a partition class.
   % Put another way, a partition indicates the subset of
   % instances of that S-Fcn that all map to one function call.
   %
   % We uniquely number each partition class in order to name
   % the corresponding generated function.  Each member of
   % the partition (e.g., each block instance) needs to access
   % this number, so we assign it to each block in the
   % SFcnPartition field.
   %
   % The field is set to -1 if it has not yet been computed,
   % e.g., in its default state.
   %
   % The field is set to 0 if no partition is created (i.e., the
   % code is inlined for the S-Fcn), or only one partition exists.
   %
   newEntry.SFcnPartition = -1;
   
   % Construct structure of param/value pairs:
   % Ex:  temp.cplx    = 0
   %      temp.inWidth = 32
   %      temp.nChans  = 1
   %
   % We need to convert from one data organization to another, and
   % this requires several (fairly advanced) manipulations:
   % 
   % 1) start with a structure containing a "CGFcnArgs" field
   %    that field is a cell array of structures.
   %    the individual structures are really all the same
   %      (containing about half a dozen fields)
   %    concatenate these into a new array of structures
   %      (instead of the cell-array of structures)
   temp = cat(1, blk.CGFcnArgs{:});
   %
   % 2) Now, reorganize this array of structures into a new structure,
   %    containing only the param fields and their associated settings:
   %
   newEntry.params = cell2struct({temp.setting},{temp.param},2);

   % Add record to database:
   %
   if isempty(db),
      db = newEntry;
   else
      db(end+1) = newEntry;
   end

   
case 'end'
   %
   % Destroy database.
   % This must occur at the end of model code generation; otherwise,
   %   the database content will persist and thus be present the next
   %   time code is generated.  At best, this would lead to an ever-
   %   increasing database size.  At worst, duplicate blocks will attempt
   %   to be added to the database, which is expressly checked and will
   %   generate an error.  The latter case would occur if you simply
   %   generate code for the same model twice in one session.
   %
   % Since there is no place to make a "cleanup" call at the
   %   very end of code generation, we must perform a call to this
   %   procedure at the end of code generation for each block.
   % The database must be maintained, however, until the last
   %   block using the database has finished generating its code.
   % Thus, we can only destroy the database if this is the last
   %   reference to the database.
   %

   db_endcnt = db_endcnt+1;
   if db_endcnt == length(db),
      % This is the last block instance tracked by this
      % code-gen database
      %
      % Reset the code-gen database, and any persistent
      % info in the query engine (dsp_cgfc):
      %
      dsp_cgfcdb('reset');
      dsp_cgfc([],'reset');

      % Indicate that this is the last instance:
      ret = 1;

   else
      % Indicate that this is NOT the last block instance:
      ret = 0;
   end
   
   
otherwise
   ret = -1;
   error('Unknown action');
   
end


% ---------------------------------------------------------------
function [db,db_endcnt] = resetDatabase
%resetDatabase Clear and initialize the database contents.

db = [];        % Clear database
db_endcnt = 0;  % Reset database reference count
return


% ---------------------------------------------------------------
function msg = checkSFcn(blk)
% CheckSFcn Verify that this block is an S-Function.
%
% blk is the TLC block data structure

msg = '';
if ~strcmp(blk.Type,'S-Function'),
   msg = 'Block must be a masked S-Function in order to use database.';
end
return


% [EOF] dsp_cgfcdb.m
