Guy on Simulink

Simulink & Model-Based Design

Bus Copies Explained!

A Simulink developer caught me in the hall last week and asked me, “How do people use the Signal Conversion block?”  He was specifically interested in how people use Contiguous Copy mode, and if people check the Override optimizations and always copy signal check box.

Signal Conversion block set to contiguous copy mode.

If you use the block in your models, please post a comment here and tell us about your use case.  In this post, I will show you one of the examples I gave him that has to do with bus copies and unravel some of the interesting semantics at work.  The models I used are available for download from the File Exchange.

Meaning of Atomic

An atomic subsystem is a Simulink semantic that means the blocks inside execute together, so you can treat them collectively as one block.  It is like a macro or function in C.  The routine executes all the code contained within and runs to completion.  When you draw a line from the output of a subsystem (A) to another block (B), you are asking for the input to B to be the output of A.   This data dependency ensures that the signal will only change if that atomic subsystem executes again.  If an input to the atomic subsystem is passes through to the output, unchanged, what happens when that input signal is changed?  Atomic blocks in Simulink must control their output signal memory. This gives rise to the following example.

A Signal Runs Through It

Here is the NoSignalConversion subsystem.  Notice it has a heavy black outline because the system is atomic.  The subsystem takes a bus as input, repackages one element from the bus with a computed value and passes it to the output.

Example atomic subsystem with inport directly passing through to the output.

Notice that the diagram is displaying little red numbers on the corners of the blocks to indicate their sorted order. Virtual blocks, like the bus selector, do not have a place in the sorted order.

When we dive into the subsystem, you can see that the Inport In1 has a sorted order label, which means it is non-virtual.  Usually Inports are virtual.  What does an input port do when it is non-virtual?  Let’s look at the code:

  109       /* Outputs for atomic SubSystem: '<Root>/NoSignalConversion' */
  110   
  111       /* Inport: '<S1>/In1' */
  112       for (i = 0; i < 6; i++) {
  113         rtb_In1[i] = rtb_FromWs[i];
  114       }
  115   
  116       /* Gain: '<S1>/Gain' */
  117       rtb_b1 = 2.0 * rtb_In1[1];
  118   
  119       /* end of Outputs for SubSystem: '<Root>/NoSignalConversion' */
  120   
  121       /* Outport: '<Root>/Out1' */
  122       ssnoSigConv_Y.Out1[0] = rtb_In1[0];
  123       ssnoSigConv_Y.Out1[1] = rtb_b1;

 

It may seem a little cryptic, but the Inport makes a copy of the memory outside the atomic subsystem into the subsystem.  This copy is the rtb_In1 temporary vector (two thirds of this vector is never used!).  In this situation, the copy is wasteful*, but it happens because of the Simulink semantic that all atomic blocks control their output memory.

Signal Conversion Block to the Rescue

We can improve our model by inserting a Signal Conversion Block.

Atomic subsystem using a Signal Conversion block to copy the one element that passes through to the output.

Inserting the signal conversion block resolves the memory pass through issue.  This prevents the Inport from becoming non-virtual, and we no longer get a copy of all input bus elements.  Here is the improved code:

  108       /* Outputs for atomic SubSystem: '<Root>/WithSignalConversion' */
  109   
  110       /* Gain: '<S2>/Gain' */
  111       rtb_b1 = 2.0 * rtb_FromWs[1];
  112   
  113       /* SignalConversion: '<S2>/TmpHiddenBufferAtSignal ConversionInport1' */
  114       rtb_TmpHiddenBufferAtSignalConv = rtb_FromWs[0];
  115   
  116       /* end of Outputs for SubSystem: '<Root>/WithSignalConversion' */
  117   
  118       /* Outport: '<Root>/Out' */
  119       sswSigConv_Y.Out[0] = rtb_TmpHiddenBufferAtSignalConv;
  120       sswSigConv_Y.Out[1] = rtb_b1;

 

Important Code Efficiency Note: If you enable the Eliminate superfluous local variables (Expression folding) optimization, both subsystems produce roughly identical code that looks like this:

  105     /* Outport: '<Root>/Out1' incorporates:
  106      *  Gain: '<S1>/Gain'
  107      *  Inport: '<S1>/In1'
  108      */
  109     ssnoSigConvExprFld_Y.Out1[0] = rtb_FromWs[0];
  110     ssnoSigConvExprFld_Y.Out1[1] = 2.0 * rtb_FromWs[1];
 

Interesting Semantics at Work

This is not the first time I have had to explain this interesting interaction of semantics.  Technical support will occasionally get calls asking why the generated code includes unnecessary bus data copies.  We even wrote a technical support solution that explains this exact phenomenon.  Here is the summary of what is happening:

Simulink requires that atomic subsystems own the memory they output. In part, this defines the system as an atomic unit. Hence, all outputs must originate from blocks inside the atomic subsystem. When an input port feeds its signal directly to an output port in the atomic subsystem, the input port becomes nonvirtual and makes a copy of its input signal. When passing bus signals through atomic subsystems, this rule still applies, and the result is often perceived as unnecessary copies of bus signals in the generated code.

We want your feedback!

This is only one instance of bus signal copies.  What other modeling constructs do you want to learn?  How do you use the signal conversion block?  Do you use the “Override optimizations and always copy signal” check box?  Share your use case and leave a comment here.

|
  • print

Comments

To leave a comment, please click here to sign in to your MathWorks Account or create a new one.