%% Generate Code That Dereferences Data from a Literal Memory Address
% This example shows how to generate code that reads the value of a signal by
% dereferencing a memory address that you specify. With this technique, you can generate a
% control
% algorithm that interacts with memory that
% your hardware populates (for example, memory that stores the output
% of an analog-to-digital converter in a microcontroller).  
%
% In this example, you generate an algorithm that acquires input data from
% a 16-bit block of memory at address |0x8675309|. Assume that a hardware device
% asynchronously populates only the lower 10 bits of the
% address. The algorithm must treat the address as read-only (|const|),
% volatile (|volatile|) data, and ignore the upper 6 bits of the address. 
%
% The generated code can access the data by defining a macro that
% dereferences |0x8675309| and masks the unnecessary bits:
%
% |#define A2D_INPUT ((*(volatile const uint16_T *)0x8675309)&0x03FF)|
%
% To configure a model to generate code that defines and uses this macro:
% 
% # Create the example model.
% # Create package to contain definitions of data class and storage class.
% # Use Custom Storage Class Designer to create a storage class.
% # Define a class to store property settings for the storage class.
% # Write Target Language Compiler (TLC) code that emits the correct C code
% for the storage class.
% # Define a class for the inport data.
% # Load the package into the Embedded Coder Dictionary.
% # Configure the model root-level inport to use the storage class.
% # Generate and inspect the code.
%
% For an example that shows how to use the Custom Storage Class Designer
% without writing TLC code, see <docid:ecoder_ug.buq4dqk-1 Create and Apply 
% a Custom Storage Class>.
%
% As an alternative to writing TLC code, you can use memory sections to
% generate code that includes pragmas. Depending on your build toolchain,
% you can use pragmas to specify a literal memory address for storing a global
% variable. For more information about memory sections, see <docid:ecoder_ug.br54zbe Control Data and Function Placement in Memory by Inserting Pragmas>.
%% Derivation of Macro Syntax
% In this example, you configure the generated code to define and use the
% dereferencing macro. To determine the correct syntax for the macro,
% start by recording the target address.
%
% |0x8675309|
% 
% Cast the address as a pointer to a 16-bit integer. Use the Simulink Coder
% data type name |uint16_T|.
%
% |(uint16_T *)0x8675309|
%
% Add the storage type qualifier |const| because the generated code must
% not write to the address. Add |volatile| because the hardware can
% populate the address at an arbitrary time.
%
% |(volatile const uint16_T *)0x8675309|
%
% Dereference the address.
%
% |*(volatile const uint16_T *)0x8675309|
%
% After the dereference operation, apply a mask to retain only the 10
% bits that the hardware populates. Use explicit parentheses to control the
% order of operations.
%
% |(*(volatile const uint16_T *)0x8675309)&0x03FF|
%
% As a safe coding practice, wrap the entire construct in another layer of
% parentheses.
%
% |((*(volatile const uint16_T *)0x8675309)&0x03FF)|
%% Create Example Model
% Create the example model |ex_memmap_simple|.
load_system('ex_memmap_simple')
set_param('ex_memmap_simple','SimulationCommand','Update')
open_system('ex_memmap_simple')
%%
% For the Inport block, set the output data type to |uint16|. Name the signal as
% |A2D_INPUT|. The |Inport| block and the signal line represent the data that the hardware
% populates.
%
% For the Gain block, set the output data type to |double|.
%% Create Package to Contain Definitions of Data Class and Storage Class
% In your current folder, create a folder named |+MemoryMap|. The folder
% defines a package named |MemoryMap|.
%
% To make the
% package available for use outside of your current folder, you can
% add the folder containing the |+MemoryMap| folder to the MATLAB path.
%% Create Storage Class
% To generate code that defines and reads |A2D_INPUT| as a
% macro, you must create a storage class that you can apply to the model 
% root-level inport. Later, you write TLC code that complements the
% storage class.
%
% Open the Custom Storage Class designer in advanced mode. To design a
% storage class that operates through custom TLC code, you must use
% the advanced mode.
%
%   cscdesigner('MemoryMap','-advanced');
%
% In the Custom Storage Class Designer, click *New*. A new custom storage
% class, |NewCSC_1|, appears in the list of storage class
% definitions.
%
% Rename the storage class to |MemoryMappedAddress|.
%
% For |MemoryMappedAddress|, on the *General* tab, set:
% 
% * *Type* to |Other|. The storage class can operate through custom
% TLC code that you write later.
% * *Data scope* to |Exported|. For data items that use this custom storage
% class, Simulink Coder generates the definition (for example, the
% |#define| statement that defines a macro).
% * *Data initialization* to |None|. Simulink Coder does not generate code
% that initializes the data item. Use this setting because this custom storage
% class represents read-only data. You do not select |Macro| because the Custom
% Storage Class Designer does not allow you to use |Macro| for signal data.
% * *Definition file* to |Specify| (leave the text box empty). For data
% items that consume memory in the generated code, *Definition file*
% specifies the |.c| source file that allocates the memory. This
% storage class yields a macro, which does not require memory.
% Typically, header files (|.h|), not |.c| files, define macros. Setting *Definition
% file* to |Specify| instead of |Instance specific| prevents users of the
% storage class from unnecessarily specifying a definition file.
% * *Header file* to |Instance specific|. To control the file placement of
% the macro definition, the user of the storage class
% must specify a header file for each data item that uses this storage class.
% * *Owner* to |Specify| (leave the text box empty). *Owner* 
% applies only to data items that consume memory.
%
% After you finish selecting the settings, click *Apply* and *Save*.
%
% Now, when you apply the storage class to a data item, such as inport |In1|, you can
% specify a header file to contain the generated macro definition. You cannot yet
% specify a memory address for the data item. To enable specification of a
% memory address, create a custom attributes class that you can associate
% with the |MemoryMappedAddress| storage class.
%% Define Class to Store Property Settings for Storage Class
% Define a MATLAB class to store additional information for data items that
% use the storage class. In this case, the additional information is
% the memory address.
%
% In the |MemoryMap| package (the |+MemoryMap| folder), create a folder named |@MemoryMapAttribs|.
%
% In the |@MemoryMapAttribs| folder, create a file named |MemoryMapAttribs|. The
% file defines a class that derives from the built-in class
% |Simulink.CustomStorageClassAttributes|.
%
% <include>MemoryMapAttribs_supp.m</include>
%
% Later, you associate this MATLAB class with the |MemoryMappedAddress| storage class.
% Then, when you apply the storage class to a data item, you can specify a memory
% address.
%% Write TLC Code That Emits Correct C Code
% Write TLC code that uses the attributes of the storage class, such
% as |HeaderFile| and |MemoryAddress|, to generate correct C code for each
% data item.
%
% In the |+MemoryMap| folder, create a folder named |tlc|. 
%
% Navigate to
% the new folder.
%
% Inspect the built-in template TLC file, <matlab:edit(fullfile(matlabroot,'toolbox','rtw','targets','ecoder','csc_templates','TEMPLATE_v1.tlc')) |TEMPLATE_v1.tlc|>.
%
%   edit(fullfile(matlabroot,...
%       'toolbox','rtw','targets','ecoder','csc_templates','TEMPLATE_v1.tlc'))
% 
% Save a copy of |TEMPLATE_v1.tlc| in the |tlc| folder. Rename the copy
% |memory_map_csc.tlc|.
%
% In |memory_map_csc.tlc|, find the portion that controls the generation of
% C-code data declarations.
file = fullfile(matlabroot,...
    'toolbox','rtw','targets','ecoder','csc_templates','TEMPLATE_v1.tlc');
rtwdemodbtype(file,'%case "declare"','The "defnComment" request',1,0)
%%
% The |declare| case (|%case|) constructs a return value (|%return|), which the code generator emits 
% into the header file that you specify for each data item. To control the
% C code that declares each data item, adjust the return value in the
% |declare| case.
%
% Replace the existing |%case| content with this new code, which specifies a different return value:
file = 'memory_map_csc_supp.tlc';
rtwdemodbtype(file,'%case "declare"','The "defnComment" request',1,0)
%%
% The new TLC code uses built-in, documented TLC functions, such as
% |LibGetRecordIdentifier|, and other TLC commands and operations to access information
% about the data item. Temporary variables such as |dt| and |address| store
% that information. The TLC code constructs the full macro, with the correct C
% syntax, by expanding the variables, and stores the macro in the variable
% |macro|.
%
% In the same file, find the portion that controls the generation of data
% definitions.
file = fullfile(matlabroot,'toolbox','rtw','targets','ecoder','csc_templates','TEMPLATE_v1.tlc');
rtwdemodbtype(file,'%case "define"','The "contents" request',1,0)
%%
% The |define| case derives a return value that the code
% generator emits into a |.c| file,
% which defines data items that consume memory.
%
% Replace the existing |%case| content with this new content:
file = 'memory_map_csc_supp.tlc';
rtwdemodbtype(file,'%case "define"','The "contents" request',1,0)
%%
% |MemoryMappedAddress| yields a macro in the generated code, so you
% use the |declare| case instead of the |define| case to construct and emit the macro. To prevent the |define|
% case from emitting a duplicate macro definition, the new TLC
% code returns an empty string.
%
% Find the portion that controls the generation of code that initializes
% data.
file = fullfile(matlabroot,'toolbox','rtw','targets','ecoder','csc_templates','TEMPLATE_v1.tlc');
rtwdemodbtype(file,'%case "initialize"','The "layout" request',1,0)
%%
% The |initialize| case generates code that initializes data items (for
% example, in the |_model__initialize| function).
%
% Replace the existing |%case| content with this new content:
file = 'memory_map_csc_supp.tlc';
rtwdemodbtype(file,'%case "initialize"','The "layout" request',1,0)
%%
% |MemoryMappedAddress| yields a macro, so the generated code must not
% attempt to initialize the value of the macro. The new TLC code returns an
% empty string.
%% Complete the Definition of the Storage Class
% Your new MATLAB class, |MemoryMapAttribs|, can enable users of
% your storage class, |MemoryMappedAddress|,
% to specify a memory address for each data item. To allow this
% specification, associate |MemoryMapAttribs| with |MemoryMappedAddress|.
% To generate correct C code based on the information that you specify for each data
% item, associate the customized TLC file, |memory_map_csc.tlc|, with
% |MemoryMappedAddress|.
%
% Navigate to the folder that contains the |+MemoryMap| folder. 
%
% Open the Custom Storage Class Designer again.
%
%   cscdesigner('MemoryMap','-advanced');
%
% For |MemoryMappedAddress|, on the *Other Attributes* tab, set:
%
% * *TLC file name* to |memory_map_csc.tlc|.
% * *CSC attributes class* to |MemoryMap.MemoryMapAttribs|.
%
% Click *Apply* and *Save*.
%% Define Signal Data Class
% To apply the storage class to a data element in a model, in the |MemoryMap| 
% package, you must create a
% MATLAB class that derives from |Simulink.Signal|. When you configure the
% signal in the model,
% you select this new data class instead of the default class,
% |Simulink.Signal|.
%
% In the |MemoryMap| package, create a folder named |@Signal|.
% 
% In the |@Signal| folder, create a file named |Signal.m|.
%
% <include>Signal_supp.m</include>
%
% The file defines a class named |MemoryMap.Signal|. The class definition overrides the |setupCoderInfo| method, which the
% |Simulink.Signal| class already implements. The new implementation
% specifies that objects of the |MemoryMap.Signal| class use custom storage
% classes from the |MemoryMap| package (instead of storage classes from the |Simulink| package).
% When you configure a signal in a model by selecting the
% |MemoryMap.Signal| class, you can select the new custom storage
% class, |MemoryMappedAddress|.
%% Load Package into Embedded Coder Dictionary
% Navigate to the folder that contains the example model and open the
% model.
%
% Open the *Embedded Coder* app. 
%
% Open the Embedded Coder Dictionary. On the *C Code* tab, select 
% *Code Interface* > *Embedded Coder Dictionary*.
% 
% In the Embedded Coder Dictionary, click *Manage Packages*.
%
% In the Manage Package dialog box, click *Refresh*. When the refresh is
% complete, select the package |MemoryMap|. Click *Load*.
%
% Close the Embedded Coder Dictionary.
%
%% Configure Root-Level Inport to Use Storage Class
% In the *Embedded Coder* app, use the Code Mappings editor and Property
% Inspector to configure Inport |In1| to use the storage class that you
% defined.
%
% Open the Code Mappings editor. On the *C Code* tab, select 
% *Code Interface* > *Individual Element Code Mappings*.
%
% In the Code Mappings Editor, on the *Inports* tab, select inport |In1|.
% Set the storage class for |In1| to |MemoryMappedAddress|.
%
% In the Property Inspector, under *Code*, set the *HeaderFile* property
% to |memory_mapped_addresses.h| and the *MemoryAddress* property to
% |0x8675309|.
%
% Save the model.
%% Generate and Inspect Code
% Generate code from the model.
mkdir('+MemoryMap')
cd('+MemoryMap')
mkdir('@Signal')
mkdir('@MemoryMapAttribs')
mkdir('tlc')
copyfile(fullfile('..','Signal_supp.m'),fullfile('@Signal','Signal.m'))
copyfile(fullfile('..','MemoryMapAttribs_supp.m'),fullfile('@MemoryMapAttribs','MemoryMapAttribs.m'))
copyfile(fullfile('..','memory_map_csc_supp.tlc'),fullfile('tlc','memory_map_csc.tlc'))
copyfile(fullfile('..','csc_registration_supp.m'),fullfile('csc_registration.m'))
cd('..')
open_system('ex_memmap_simple')
portHandles = get_param('ex_memmap_simple/In1','PortHandles');
outportHandle = portHandles.Outport;
temp = MemoryMap.Signal;
temp.CoderInfo.StorageClass = 'Custom';
temp.CoderInfo.CustomStorageClass = 'MemoryMappedAddress';
temp.CoderInfo.CustomAttributes.HeaderFile = 'memory_mapped_addresses.h';
temp.CoderInfo.CustomAttributes.MemoryAddr = '0x8675309';
set_param(outportHandle,'SignalObject',temp)
rtwbuild('ex_memmap_simple');
%%
% Inspect the generated header file |memory_mapped_addresses.h|. The file
% defines the macro |A2D_INPUT|, which corresponds to the signal line in
% the model.
file = fullfile('slprj','ert','_sharedutils','memory_mapped_addresses.h');
rtwdemodbtype(file,'/* Declaration of data with custom storage class MemoryMappedAddress */',...
    '#define A2D_INPUT',1,1)
%%
% Inspect the generated file |ex_memmap_simple.c|. The generated
% algorithmic code (which corresponds to the Gain block) calculates the
% model output, |rtY.Out1|, by operating on |A2D_INPUT|.
file = fullfile('ex_memmap_simple_ert_rtw','ex_memmap_simple.c');
rtwdemodbtype(file,'/* Model step function */','/* Model initialize function */',1,0)


%% 
% Copyright 2020 The MathWorks, Inc.