问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Simulink相关配置: Optimization配置

创作时间:
作者:
@小白创作中心

Simulink相关配置: Optimization配置

引用
CSDN
1.
https://blog.csdn.net/u013288925/article/details/106695439

本文研究Embedded Coder中的Optimization配置,通过一些模型示例和代码直观地比较配置对代码生成的影响。由于配置选项很多,本文会长期更新。

Optimization配置

Optimization配置中包含了代码生成的优化选项。在Simulink配置窗口的Code Generation下可以找到Optimization配置。

后文会研究Optimization配置中的一些常用选项。

Default parameter behavior

该配置直译过来是“默认参数行为”,其含义是,生成代码时常量参数的形式。

该配置中包含两个选项:Tunable和Inlined。如果选Tunable,意为可调式,生成的代码就会以StorageClass中的Auto类来表现常数参数。如果选Inlined,意为内联式,会把参数“内联”到代码中,表现为直接的数值。

配置中默认为Inlined。

打开Simulink,建立一个简单的带有Gain模块的模型。

将Gain模块的增益系数写为3.观察后续这个系数在代码中的表现方式。

1)当Default parameter behavior选为Tunable时,生成的代码如下:

可以看出,输入变量乘以一个系数demo_P.Gain_Gain。这个变量在demo_data.c中定义。

2)当Default parameter behavior选为Inlined时,生成的代码如下:

这里可以看出,系数直接写成了3.0F这个浮点数。也就是说,把Gain参数“内联”到代码中了。

比较两种代码生成的方式,显然是Inlined更好。这是因为将参数生成全局变量会占据芯片的RAM资源。

在企业级项目中,每个模型都可能有数十个这种常量,广泛地存在于Constant,Gain等模块中。如果这些常量占据了大量的RAM资源,就可能会导致链接过程中出现资源溢出。

Pass reusable subsystem outputs as

这个配置项影响了可复用子系统的输出生成的代码。

下拉框包含两个选项。默认的是Individual arguments,指的是输出为局部变量。还有一个选项是Structure reference,他会生成一个全局结构体变量,然后可复用子系统对应的函数会调用这个结构体的指针。

建立如下图模型,将两个完全相同的子系统配置为原子子系统以及可复用函数。这样模型就符合这个配置项的场景了。

原子子系统内随便什么模型都可以,这里用了个比较简单的Gain模块。

1)当Pass reusable subsystem outputs as这个配置选为Individual arguments时,代码生成如下:

可以看出,在只有一个返回值的情况下,子系统所对应的函数直接把计算结果返回。在step函数中直接把结果赋值给Out1或者Out2。

因为模型比较简单,在step函数中把局部变量优化掉了。如果比较复杂的模型会把demo_Subsystem的结果先赋值给一个局部变量,然后参与后续的运算。

2)当Pass reusable subsystem outputs as这个配置选为Structure reference时,代码生成如下:

可以看出函数复杂了一些。首先是生成了一个结构体的全局变量demo_B,这里面存的是子系统的输出值。然后子系统对应的函数,用了个结构体指针获取返回值。

再看看下图中的Step函数:

step函数是先用demo_Subsystem这个函数更新了全局变量结构体里的成员,然后再把成员变量的值赋值给Out1或Out2。这样,在这个过程中就没有局部变量了,而是多了全局变量。

正所谓鱼和熊掌不可兼得,这两种方式就是对局部变量和全局变量的取舍。多生成一些全局变量,就用Structure reference,多生产一些局部变量,就用Individual arguments。

博主根据个人经验认为,汽车ECU软件一般已经用了很多全局变量,包括模型输入输出接口和观测量等都是全局变量。所以全局变量占据的RAM资源是比较稀缺和紧张的,这里按照Simulink默认的Individual arguments配置比较好,也就是多生成些局部变量。

而且,从代码复杂度的角度来说,也是Individual arguments配置更优。

Remove root level I/O zero initialization

这个配置决定了生成的代码在initialize函数中是否有对根路径输入输出的零初始化。

模型其实很简单,只要根路径下有个输入输出的模块就行。

当不勾选Remove root level I/O zero initialization的时候,会在初始化函数中将输入输出模块的变量初始化为0.

当勾选Remove root level I/O zero initialization的时候,初始化函数中不会有对输入输出的零初始化。

个人认为这一项可以勾选,也就是说不在初始化函数中将输入输出初始化为零。

因为在ECU中,我们生成的函数不断地被OS调度,每个采样周期都会赋给新的数值,而大多数时候不会关注他们的初始值。对于需要定义初始值的信号,一般也会在模型中做好初始化的子系统,以保证某个信号生成代码的时候赋给特定的初值而不是零。

所以没有必要生成零初始化,这样也能减少一些Flash的占用,提高代码生成效率。虽然微不足道。

除了一种特殊的情况,如果控制器因为某种原因,复位后没有将RAM中的数值清零。这时候就需要这部分的初始化函数。

Remove internal data zero initialization

这个配置决定了生成的代码在initialize函数中是否对内部变量进行零初始化。

内部变量的类型有很多种,这里用一个Unit Delay为例。由于Unit Delay实现的是一个延时效果,所以生成的代码会通过一个全局变量来存储上一个周期的数值。这个全局变量就是一种内部变量。建一个简单的模型如下:

当不勾选Remove internal data zero initialization的时候,会在初始化函数中将Unit Delay模块内部的全局变量初始化为0。下图中用了一个memset函数。

当勾选了Remove internal data zero initialization的时候,初始化函数中就不会把Unit Delay模块内部的全局变量初始化为0。

同4.4一样,博主也认为这条可以勾选。但是还是要根据实际项目需求来看,因为博主也只是基于自身项目团队的经验所得出的结论。

Use memcpy for vector assignment

该选项控制生成代码时,是否用memcpy函数代替for循环对数组赋值。

在模型中构建出Vector信号,在生成的代码中就会有数组的赋值操作。搭建如下示例模型:

1)在空白的Simulink中搭建一个Inport模块、一个Selector模块和一个Outport模块。

2)将Inport模块的Dimension配置为[1X100],Output DataType配置为int32。

3)将Selector配置如下,表示取出输入信号的前50个元素。

4)仿真模型后,显示出Vector信号的长度以及数据类型。

1)当勾选上Use memcpy for vector assignment时,生成的代码如下:

很容易看出,step函数中用了memcpy函数对输出Out1的数组进行赋值。

2)当取消勾选Use memcpy for vector assignment时,生成的代码如下:

可以看出,step函数中用了一个for循环对输出Out1逐个赋值。

通过memcpy对数组赋值会比用for循环的方式执行速度更快,所以勾选这一项比较合理。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号