![]() |
RZ/A Flexible Software Package Documentation
Release v3.4.0
|
|
This guide describes the Renesas Flexible Software Package (FSP) architecture and how to use the FSP Application Programming Interface (API).
The FSP uses the ISO/IEC 9899:1999 (C99) C programming language standard. Specific features introduced in C99 that are used include standard integer types (stdint.h), booleans (stdbool.h), designated initializers, and the ability to intermingle declarations and code.
Doxygen is the default documentation tool used by FSP. You can find Doxygen comments throughout the FSP source.
Weak symbols are used occasionally in the FSP. They are used to ensure that a project builds even when the user has not defined an optional function.
Dynamic memory allocation through use of the malloc() and free() functions are not used in FSP modules; all memory required by FSP modules is allocated in the application and passed to the module in a pointer. Exceptions are considered only for ports of 3rd party code that require dynamic memory.
Term | Description | Reference |
---|---|---|
BSP | Short for Board Support Package. In the FSP the BSP provides just enough foundation to allow other FSP modules to work together without issue. | MPU Board Support Package |
Module | Modules can be peripheral drivers, purely software, or anything in between. Each module consists of a folder with source code, documentation, and anything else that the customer needs to use the code effectively. Modules are independent units, but they may depend on other modules. Applications can be built by combining multiple modules to provide the user with the features they need. | FSP Modules |
Driver | A driver is a specific kind of module that directly modifies registers on the MPU. | - |
Interface | An interface contains API definitions that can be shared by modules with similar features. Interfaces are definitions only and do not add to code size. | FSP Interfaces |
Stacks | The FSP architecture is designed such that modules work together to form a stack. A stack consists of a top level module and all its dependencies. | FSP Stacks |
Module Instance | Single and independent instantiation of a module. An application may require two GPT timers. Each of these timers is a module instance of the r_gpt module. | - |
Application | Code that is owned and maintained by the user. Application code may be based on sample application code provided by Renesas, but it is the responsibility of the user to maintain as necessary. | - |
Callback Function | This term refers to a function that is called when an event occurs. As an example, suppose the user would like to be notified every second based on the RTC. As part of the RTC configuration, a callback function can be supplied that will be jumped to during each RTC interrupt. When a single callback services multiple events, the arguments contain the triggering event. Callback functions for interrupts should be kept short and handled carefully because when they are called the MPU is still inside of an interrupt, delaying any pending interrupts. | - |
Modules are the core building block of FSP. Modules can do many different things, but all modules share the basic concept of providing functionality upwards and requiring functionality from below.
The amount of functionality provided by a module is determined based on functional use cases. Common functionality required by multiple modules is often placed into a self-contained submodule so it can be reused. Code size, speed and complexity are also considered when defining a module.
The simplest FSP application consists of one module with the Board Support Package (BSP) and the user application on top.
The Board Support Package (BSP) is the foundation for FSP modules, providing functionality to determine the MPU used as well as configuring clocks, interrupts and pins. For the sake of clarity, the BSP will be omitted from further diagrams.
When modules are layered atop one another, an FSP stack is formed. The stacking process is performed by matching what one module provides with what another module requires.
At the architecture level, interfaces are the way that modules provide common features. This commonality allows modules that adhere to the same interface to be used interchangeably. Interfaces can be thought of as a contract between two modules - the modules agree to work together using the information that was established in the contract.
Interfaces aim to provide support for the common features that most users would expect. This means that some of the advanced features of a peripheral (such as IIC) might not be available in the interface. In most cases these features are still available through interface extensions.
In FSP design, interfaces are defined in header files. All interface header files are located in the folder rza/fsp/inc/api
and end with *_api.h
. Interface extensions are defined in header files in the folder rza/fsp/inc/instances
. The following sections detail what makes up an interface.
Whenever possible, interfaces use typed enumerations for function parameters and structure members.
Enumerations remove uncertainty when deciding what values are available for a parameter. FSP enumeration options follow a strict naming convention where the name of the type is prefixed on the available options. Combining the naming convention with the autocomplete feature available in e2 studio (Ctrl + Space) provides the benefits of rapid coding while maintaining high readability.
Callback functions allow modules to asynchronously alert the user application when an event has occurred, such as when a byte has been received over a UART channel or an IRQ pin is toggled. FSP driver modules define and handle the interrupt service routines for RZ MPU peripherals to ensure any required hardware procedures are implemented. The interrupt service routines in FSP modules then call the user-defined callbacks to allow the application to respond.
Callback functions must be defined in the user application. They always return void
and take a structure for their one parameter. The structure is defined in the interface for the module and is named <interface>_callback_args_t
. The contents of the structure may vary depending on the interface, but two members are common: event
and p_context
.
The event
member is an enumeration defined in the interface used by the application to determine why the callback was called. Using the UART example, the callback could be triggered for many different reasons, including when a byte is received, all bytes have been transmitted, or a framing error has occurred. The event
member allows the application to determine which of these three events has occurred and handle it appropriately.
The p_context
member is used for providing user-specified data to the callback function. In many cases a callback function is shared between multiple channels or module instances; when the callback occurs, the code handling the callback needs context information so that it can determine which module instance the callback is for. For example, if the callback wanted to make an FSP API call in the callback, then at a minimum the callback will need a reference to the relevant control structure. To make this easy, the user can provide a pointer to the control structure as the p_context
. When the callback occurs, the control structure is passed in the p_context
element of the callback structure.
When a module is not directly used in the user application (that is, it is not the top layer of the stack), its callback function will be handled by the module above. For example, if a module requires a UART interface module the upper layer module will control and use the UART's callback function. In this case the user would not need to create a callback function for the UART module in their application code.
At a minimum, all FSP interfaces include three data structures: a configuration structure, an API structure, and an instance structure.
The configuration structure is used for the initial configuration of a module during the <MODULE>_Open()
call. The structure consists of members such as channel number, bitrate, and operating mode.
The configuration structure is used purely as an input into the module. It may be stored and referenced by the module, so the configuration structure and anything it references must persist as long as the module is open.
The configuration structure is allocated for each module instance in files generated by the RZ Configuration editor.
When FSP stacks are used, it is also important to understand that configuration structures only have members that apply to the current interface. If multiple layers in the same stack define the same configuration parameters then it becomes difficult to know where to modify the option. For example, the baud rate for a UART is only defined in the UART module instance. Any modules that use the UART interface rely on the baud rate being provided in the UART module instance and do not offer it in their own configuration structures.
All interfaces include an API structure which contains function pointers for all the supported interface functions.
Every FSP interface also has an instance structure. The instance structure encapsulates everything required to use the module:
The instance structure is not required at the application layer. It is used to connect modules to their dependencies (other than the BSP).
Instance structures have a standardized name of <interface>_instance_t
.
Note that when an instance structure variable is declared, the API is the only thing that is instance specific, not module instance specific. This is because all module instances of the same module share the same underlying module source code. If SPI is being used on SCI channels 0 and 2 then both module instances use the same API while the configuration and control structures are typically different.
While interfaces dictate the features that are provided, instances actually implement those features. Each instance is tied to a specific interface. Instances use the enumerations, data structures, and API prototypes from the interface. This allows an application that uses an interface to swap out the instance when needed.
In FSP design, instances consist of the interface extension and API defined in the instance header file located in the folder rza/fsp/inc/instances
and the module source rza/fsp/src/<module>
.
The control structure is used as a unique identifier for the module instance and contains memory required by the module. Elements in the control structure are owned by the module and must not be modified by the application. The user allocates storage for a control structure, often as a global variable, then sends a pointer to it into the <MODULE>_Open()
call for a module. At this point, the module initializes the structure as needed. The user must then send in a pointer to the control structure for all subsequent module calls.
In some cases, instances require more information than is provided in the interface. This situation can occur in the following cases:
The p_extend
member provides this extension function.
Use of interface extensions is not always necessary. Some instances do not offer an extension since all functionality is provided in the interface. In these cases the p_extend
member can be set to NULL. The documentation for each instance indicates whether an interface extension is available and whether it is mandatory or optional.
When extended configuration is required it can be supplied through the p_extend
parameter of the interface configuration structure.
The extended configuration structure is part of the instance, but it is also still considered to be part of the configuration structure. All usage notes about the configuration structure described in FSP Interface Configuration Structure apply to the extended configuration structure as well.
The extended configuration structure and all typed structures and enumerations required to define it make up the interface extension.
Each instance includes a constant global variable tying the interface API functions to the functions provided by the module. The name of this structure is standardized as g_<interface>_on_<instance>
.
FSP functions start with the uppercase module name (<MODULE>
). All modules have <MODULE>_Open()
and <MODULE>_Close()
functions. The <MODULE>_Open()
function must be called before any of the other functions.
Other functions that will commonly be found are <MODULE>_Read()
, <MODULE>_Write()
, <MODULE>_InfoGet()
, and <MODULE>_StatusGet()
. The <MODULE>_StatusGet()
function provides a status that could change asynchronously, while <MODULE>_InfoGet()
provides information that cannot change after open or can only be updated by API calls. Example function names include:
The const
qualifier is used with API parameters whenever possible.
The const
qualifier after the pointer star for both p_ctrl
and p_cfg
ensures the FSP function does not modify the input pointer addresses. While not fool-proof by any means this does provide some extra checking inside the FSP code to ensure that arguments that should not be altered are treated as such.
The BSP provides a function R_FSP_VersionGet()
which fills in a structure of type fsp_pack_version_t
. This can be used to determine the FSP version at runtime.
There are also FSP_VERSION_*
macros in fsp_version.h that can be used to determine the FSP version at build time.
All modules have a build-time configuration header file. Most configuration options are supplied at run time, though options that are rarely used or apply to all instances of a module may be moved to build time. The advantage of using a build-time configuration option is to potentially reduce code size reduction by removing an unused feature.
All modules have a build time option to enable or disable parameter checking for the module. FSP modules check function arguments for validity when possible, though this feature is disabled by default to reduce code size. Enabling it can help catch parameter errors during development and debugging. By default, each module's parameter checking configuration inherits the BSP parameter checking setting (set on the BSP tab of the RZ Configuration editor). Leaving each module's parameter checking configuration set to Default (BSP) allows parameter checking to be enabled or disabled globally in all FSP code through the parameter checking setting on the BSP tab.
If an error condition can reasonably be avoided it is only checked in a section of code that can be disabled by disabling parameter checking. Most FSP APIs can only return FSP_SUCCESS if parameter checking is disabled. An example of an error that cannot be reasonably avoided is the "bus busy" error that occurs when another master is using an I2C bus. This type of error can be returned even if parameter checking is disabled.
The high-level file structure of an FSP project is shown below.
Directly underneath the base rza
folder the folders are split into the source and include folders. Include folders are kept separate from the source for easy browsing and easy setup of include paths.
The rza_gen
folder contains code generated by the RZ Configuration editor. This includes global variables for the control structure and configuration structure for each module.
The rza_cfg
folder is where configuration header files are stored for each module. See FSP Build Time Configurations for information on what is provided in these header files.
FSP modules are meant to be both reusable and stackable. It is important to remember that modules are not dependent upon other modules, but upon other interfaces. The user is then free to fulfill the interface using the instance that best fits their needs.
In the image above interface Y is a dependency of interface X and has its own dependency on interface Z. Interface X only has a dependency on interface Y. Interface X has no knowledge of interface Z. This is a requirement for ensuring that layers can easily be swapped out.
The typical use of an FSP module involves generating required module data then using the API in the application.
The RZ Configuration editor provides a graphical user interface for setting the parameters of the interface and instance configuration structures. It also automatically includes those structures (once they are configured in the GUI) in application-specific header files that can be included in application code.
The RZ Configuration editor allocates storage for the control structures, all required configuration structures, and the instance structure in generated files in the rza_gen
folder. Use the Properties window to set the values for the members of the configuration structures as needed. Refer to the Configuration section of the module usage notes for documentation about the configuration options.
If the interface has a callback function option then the application must declare and define the function. The return value is always of type void
and the parameter to the function is a typed structure of name <interface>_callback_args_t
. Once the function has been defined, assign its name to the p_callback
member of the configuration structure. Callback function names can be assigned through the Properties window for the selected module.
Call the module's <MODULE>_Open()
function. Pass pointers to the generated control structure and configuration structure. The names of these structures are based on the 'Name' field provided in the configuration editor. The control structure is <Name>_ctrl
and the configuration structure is <Name>_cfg
.
Refer to the module usage notes for example code to help get started with any particular module.