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.
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.
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.
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.
- Category:
- Code Generation,
- Signals,
- Simulink Tips
Comments
To leave a comment, please click here to sign in to your MathWorks Account or create a new one.