Simulink代码生成: Optimization配置

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

1 Optimization配置

Optimization配置中包含了代码生成的优化选项。在Simulink配置窗口的Code Generation下可以找到Optimization配置。
在这里插入图片描述
后文会研究Optimization配置中的一些常用选项。

2 Default parameter behavior

2.1 描述

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

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

配置中默认为Inlined。
在这里插入图片描述

2.2 模型示例

打开Simulink,建立一个简单的带有Gain模块的模型。
在这里插入图片描述
将Gain模块的增益系数写为3.观察后续这个系数在代码中的表现方式。
在这里插入图片描述

2.3 生成代码

1)当Default parameter behavior选为Tunable时,生成的代码如下:
在这里插入图片描述
可以看出,输入变量乘以一个系数demo_P.Gain_Gain。这个变量在demo_data.c中定义。
在这里插入图片描述
2)当Default parameter behavior选为Inlined时,生成的代码如下:
在这里插入图片描述
这里可以看出,系数直接写成了3.0F这个浮点数。也就是说,把Gain参数“内联”到代码中了。

2.4 分析与思考

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

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

3 Pass reusable subsystem outputs as

3.1 描述

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

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

3.2 模型示例

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

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

3.3 生成代码

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。这样,在这个过程中就没有局部变量了,而是多了全局变量。

3.4 分析与思考

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

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

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

4 Remove root level I/O zero initialization

4.1 描述

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

4.2 模型示例

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

4.3 生成代码

当不勾选Remove root level I/O zero initialization的时候,会在初始化函数中将输入输出模块的变量初始化为0.
在这里插入图片描述
当勾选Remove root level I/O zero initialization的时候,初始化函数中不会有对输入输出的零初始化。
在这里插入图片描述

4.4 分析与思考

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

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

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

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

5 Remove internal data zero initialization

5.1 描述

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

5.2 模型示例

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

5.3 生成代码

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

5.4 分析与思考

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

6 Use memcpy for vector assignment

6.1 描述

该选项控制生成代码时,是否用memcpy函数代替for循环对数组赋值。
在这里插入图片描述

6.2 模型示例

在模型中构建出Vector信号,在生成的代码中就会有数组的赋值操作。搭建如下示例模型:
1)在空白的Simulink中搭建一个Inport模块、一个Selector模块和一个Outport模块。
在这里插入图片描述
2)将Inport模块的Dimension配置为[1X100],Output DataType配置为int32。
在这里插入图片描述
3)将Selector配置如下,表示取出输入信号的前50个元素。
在这里插入图片描述
4)仿真模型后,显示出Vector信号的长度以及数据类型。
在这里插入图片描述

6.3 生成代码

1)当勾选上Use memcpy for vector assignment时,生成的代码如下:
在这里插入图片描述
很容易看出,step函数中用了memcpy函数对输出Out1的数组进行赋值。

2)当取消勾选Use memcpy for vector assignment时,生成的代码如下:
在这里插入图片描述
可以看出,step函数中用了一个for循环对输出Out1逐个赋值。

6.4 分析与思考

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

  • 26
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值