Interface Design

Why Interface Definitions Are Important

Defining the “interface” of a software component, such as a C or MATLAB® code function or a Simulink® subsystem, is a key first step before others can use it, for these reasons:

  • Agreeing on an interface is a useful first step in deciding how to break down the functionality of a large system into subcomponents.

  • After you define interfaces between a number of components, you can develop the components in parallel. If the interface remains stable, then it is easy to integrate those components into a larger system.

  • Changing the interface between components is expensive. It requires changes to at least two components (the source and any sinks) and to any test harnesses. It also makes all previous versions of those components incompatible with the current and future versions.

When you need to change an interface, doing so is much easier if the components are stored under configuration management. You can track configurations of compatible component versions to prevent incompatible combinations of components.

Recommendations for Interface Design

Suggestions for defining the interfaces of components for a new project:

  • Base the boundaries of the components upon those of the corresponding real systems. This is especially useful when the model contains:

    • Both physical (plant and environment) and control systems

    • Algorithms that run at different rates

  • Consider future model elaboration. If you intend to add models of sensors, then put them in from the start as an empty subsystem that passes signals straight through or performs a unit delay and/or name conversion.

  • Consider future component reuse.

  • Consider using a signal naming convention.

  • Use data objects for :

    • Defining component interfaces

    • Precise control over data attributes

  • Simplify interface design by grouping signals into buses. Signal buses are well suited for use at the high levels of models, where components often have a large number of signals going in and out, and do not use all the signals available. Using buses can simplify modifying the interface to a component. For example, if you need to add or remove signals used by a component, it can be simpler to modify a bus than to add or remove inports or outports to that component. However, using a bus that crosses model reference boundaries requires using a bus object.

    Best practices for using Simulink bus signals and bus objects:

    • Make buses virtual, except for model reference component boundaries.

    • Use nonvirtual buses when defining interfaces between components. However, this requires associating the bus with a bus object. Bus objects completely define the properties of the signals on a bus, giving an unambiguous interface definition.

      Include bus objects in a data dictionary, or save bus objects as a .mat or .m file, in order to place them under revision control.

    • Pass only required signals to each component to reduce costly passing of unnecessary data. Signal buses allow the full set of input and output signals to be defined, but not necessarily used or created.

    • Make sure that the interface specifies exactly what the component uses.

    • Use a rigorous naming convention for bus objects. Unless you use a data dictionary, bus objects are stored in the base workspace.

    • At the lower levels of a model, consider using inports and outports for each signal. At lower levels of a model, where components typically implement algorithms rather than serve as containers for other components, it can increase readability if you use individual inports and outports for components, instead of using signal buses. However, creating interfaces in this way has a greater risk of connection problems, because it is difficult to check the validity of connections, other than their data type, size, etc.

    • To package signals or parameters into structures that correspond to a struct type definition that your external C code defines, import the type as a bus object and use the object as a data type for bus signals and MATLAB structures. To create the object, use the Simulink.importExternalCTypes function.

Partitioning Data

Explicitly control the scope of data for your components. Some techniques:

  • Global parameters — A common approach in the automotive world is to completely separate the problem of parameter storage from model storage. The parameters for a model come from a database of calibration data, and the specific calibration file used becomes part of the configuration. The calibration data is treated as global data, and resides in the base MATLAB workspace. You can migrate base workspace data to a data dictionary for more control.

  • Nonglobal parameters — Combining a number of components that somehow store their own parameter data has the risk of parameter name collisions. If you do not use a naming convention for parameters or, alternatively, a list of unique parameter names and definitions, then there is the risk that two components use a parameter with the same name but with different meanings.

    Methods for storing local parameter data include:

    • Partition data into reference dictionaries for each component.

    • With Model Reference, you can use model workspaces.

    • Use parameter files (.m or .mat) and callbacks of the individual Simulink models (e.g., preload function).

      You can also automatically load required data using project shortcuts.

    • Mask workspaces, with or without the use of mask initialization functions.

    • For subsystems, you can control the scope of data for a subsystem using the Subsystem Parameters, Permit Hierarchical Resolution dialog box.

Configure Data Interface for Component

Whether you use referenced models or subsystems to break a large system into components, the components can exchange signal data through Inport and Outport blocks. You can explicitly configure design attributes (such as data type and numeric complexity) of the interface to prevent modeling errors and make integrating the components easier.

After you create the Inport and Outport blocks, you can use the Model Data Editor and the interface display to configure the design attributes (such as data type and numeric complexity) of the blocks. Use this technique to view the component interface in its entirety at once and to trace the pieces of the interface to usage points in the internal block algorithm. You can also use this technique to configure the interface of a component before you develop the internal algorithm, in which case the component contains unconnected Inport and Outport blocks.

The example model sldemo_fuelsys_dd contains two components which are referenced models:

  • A plant component, sldemo_fuelsys_dd_plant.

  • A controller component, sldemo_fuelsys_dd_controller.

Use the Model Data Editor and the interface display to examine and configure the interface of the plant component.

  1. Open the plant component.

    sldemo_fuelsys_dd_plant

  2. Select Display > Interface.

  3. Select View > Model Data Editor.

    By default, in the Model Data Editor, the Inports/Outports tab is selected. Each row in the table represents an Inport or Outport block. By default, the Change view drop-down list is set to Design.

    Tip

    To view only the Inport and Outport blocks at the root level of the model (by excluding the blocks inside the subsystems), deactivate the Change Scope button.

  4. Use the columns in the Model Data Editor to explicitly configure the design attributes of the interface. For example, specify minimum and maximum values for each Inport and Outport block by using the Min and Max columns.

To configure code generation settings for the interface of a controller component, in the Model Data Editor, set the Change view drop-down list to Code.

For more information about using the interface display, see Trace Connections Using Interface Display. For more information about the Model Data Editor, see Configure Data Properties by Using the Model Data Editor.

Related Examples

More About