技术文章和通讯

转换MATLAB代码到定点的最佳实践

哈什塔·布拉特(Harshita Bhurat)、汤姆·布莱恩(Tom Bryan)和朱莉娅·沃尔(Julia Wall), MathWorks出版


当将浮点实现转换为定点时,工程师必须确定最优的定点数据类型,满足嵌入式硬件的约束条件,同时满足系统对数字精度的要求。fixed-point Designer™通过自动提出数据类型和定点属性,并允许将位真定点模拟结果与浮点基线进行比较,帮助您开发定点算法并将浮点算法转换为定点算法。

本文概述了准备MATLAB的最佳实践®代码转换,将MATLAB代码转换为定点,并优化您的算法的效率和性能。无论您是在MATLAB中设计定点算法,为手工编码做准备,还是转换为代码生成的定点,这些最佳实践将帮助您将通用的MATLAB代码转换为高效的定点实现。

准备固定点转换代码

您可以采取三个步骤以确保顺利转换过程:

  • 将核心算法与其他代码分开。
  • 为插装和加速准备代码。
  • 检查您用于固定点支持的功能。金宝app

将核心算法与其他MATLAB代码分离

通常,算法伴随着设置输入数据的代码和创建图表以验证结果的代码。由于只需要将算法代码转换为固定点,因此更有效的方法是构造代码,以便测试文件创建输入、调用核心算法并绘制结果,而算法文件执行核心处理(表1)。

原始代码
%测试输入x = randn (100 1);%的算法y = 0(大小(x));y = x (1) (1);N =2:长度y(N) = y(N -1) + x(N);结束%验证结果yExpected = cumsum (x);情节(y-yExpected)标题(“错误”
修改后的代码

测试文件。

%测试输入x = randn (100 1);%的算法y = cumulative_sum (x);%验证结果yExpected = cumsum (x);情节(y-yExpected)标题(“错误”

算法文件。

函数y =累积_sum(x)y = zeros(size(x));y = x (1) (1);N =2:长度y(N) = y(N -1) + x(N);结束结束

为仪表和加速准备算法代码

测量和加速有助于简化转换过程。您可以使用定点设计器来检测代码,并记录所有命名变量和中间变量的最小值和最大值。该工具可以使用这些记录的值来建议在定点代码中使用的数据类型。

使用固定点设计器,您还可以通过创建MEX文件来加速您的固定点算法,并加快验证原始版本的固定点实现所需的模拟。

插装和加速都依赖于代码生成技术,所以在使用它们之前,您必须准备好代码生成的算法,即使您不打算使用MATLAB Coder™或HDL Coder™来生成C或HDL代码。首先,确定MATLAB代码中不支持代码生成的函数或构造(参见金宝app语言支持金宝app获取支持的函数和对象列表)。金宝app

有两种方法可以自动执行此步骤:

  • 添加% # codegenpragma到包含核心算法代码的MATLAB文件的顶部。这个编译指示指示代码分析仪标记代码生成支持的MATLAB语言子集中不包含的函数和构造。金宝app
  • 使用代码生成准备工具生成一个报告,该报告标识对函数的调用和代码生成不支持的数据类型的使用。金宝app

准备好代码生成算法后,您可以将固定点设计器用于乐器并加速它。使用buildInstrumentedMex启用检测以记录所有命名变量和中间变量的最小值和最大值,并使用showInstrumentationResults要查看具有建议数据类型的代码生成报告,用于固定点代码。邀请FIACCEL.将MATLAB算法转换为MEX文件并加速定点模拟。

检查你的算法代码中使用的函数的定点支持金宝app

如果要标识不支持的固定点,则有三个选项:金宝app

  • 将函数替换为等价的定点。
  • 编写自己的等价函数。
  • 将不支持的函数与输入处的双精度金宝app转换和输出处的定点类型转换进行隔离。

然后,您可以继续将代码转换为固定点,并在有合适的替代时返回到不支持的函数(表2)。金宝app

原始代码
y = 1 / exp (x);
修改后的代码
y = 1 / exp(双(x));

管理数据类型和控制位增长

在固定点实现中,定点变量必须保持固定点,而不是无意中变成双打。它也很重要防止一些增长.例如,考虑下面这行代码:

Y = Y + x(n)

这个语句覆盖y价值y + x (n).在代码中引入定点数据类型时,y可能在覆盖时更改数据类型,从而可能导致位增长。

保留数据类型y使用(,) =语法(表3)。这个语法,称为下标作业,指示MATLAB保留被覆盖变量的现有数据类型和数组大小。该声明Y (:) = Y + x(n)将右边的值转换成y原始数据类型并防止位增长。

原始代码
y = 0;n = 1:长度(x)y = y + x(n);结束
修改后的代码
y = 0;N =1:length(x) y(:) = y + x(N);结束

创建一个类型表来分离数据类型定义和算法代码

将数据类型定义与算法代码分离,可以更容易地比较定点实现,并将算法重新定位到不同的设备。

要应用此最佳实践,请执行以下操作:

  1. 使用铸造(x,“喜欢”,y)0 (m, n,“喜欢”,y)在首次定义变量时将其强制转换为所需的数据类型。
  2. 创建数据类型定义表,从代码中使用的原始数据类型开始 - 通常是双重精度浮点,MATLAB中的默认数据类型(表4A)。
  3. 在转换为定点之前,添加单身的数据类型为类型表找到类型不匹配和其他问题(表4B)。
  4. 通过运行连接到具有不同数据类型的每个表的代码并比较结果来验证连接。
原始代码
%的算法n = 128;y = 0(大小(x));
修改后的代码
%的算法T = mytypes (“双”);n =投(128'喜欢', T.n);y = 0(大小(x),'喜欢', T.y);%类型表函数T = mytypes (dt)开关(dt)案件“双”双(T.n = []);T.y =双([]);结束结束
原始代码
%类型表函数T = mytypes (dt)开关(dt)案件“双”双(T.n = []);T.y =双([]);结束结束
修改后的代码
函数T = mytypes (dt)开关(dt)案件“双”双(T.n = []);T.y =双([]);案件'单身的'T.n =单([]);T.y =单([]);结束结束

将定点条目添加到类型表中

创建数据类型定义表后,您可以根据您的目标添加定点类型以转换为固定点。例如,如果您计划在C中实现算法,则固定点类型的字长度将被约束到16的倍数。另一方面,如果您计划在HDL中实现,则单词长度不会受约束.

要为您的代码获取一组定点类型建议,请使用定点设计器命令buildInstrumentedMexshowInstrumentationResults(表5)。您将需要一组测试向量,该测试向量练习全方位的类型 - 定点设计者提出的类型仅与测试输入一样好。长期仿真运行,具有广泛的预期数据将产生更好的提案。从代码生成报告的提案中选择一组初始定点类型(图1)。

图1所示。由showInstrumentationResults生成的代码生成报告,其中包含过滤算法中变量的建议数据类型。
图1所示。由showInstrumentationResults生成的代码生成报告,其中包含过滤算法中变量的建议数据类型。

然后您可以根据需要调整建议的类型(表5和表6)。

算法代码
函数[y,z] = myfilter(b,x,z) y = 0 (size(x));N =1:length(x) z(:) = [x(N)];z (1:结束1)];Y (n) = b * z;结束结束
测试文件
%测试输入0.25 b = fir1(11日);t = linspace(0, 10 *π,256);x =罪((π/ 16)* t。^ 2);%线性啁啾z = 0(大小(b));%建立buildInstrumentedMexmyfilter ...  -  args {b,x,z} -histogram运行%[y, z] = myfilter_mex (b, x, z);% 展示showInstrumentationResultsmyfilter_mex……-proposeFL -defaultDT numerictype (16)

表5所示。一个过滤算法和测试脚本,用于检测和执行代码,并显示变量的建议定点类型。

算法代码
函数[y,z] = myfilter(b,x,z,t)y = zeros(大小(x),'喜欢', T.y);N =1:length(x) z(:) = [x(N)];z (1:结束1)];Y (n) = b * z;结束结束
测试文件
%测试输入0.25 b = fir1(11日);t = linspace(0, 10 *π,256);x =罪((π/ 16)* t。^ 2);%线性啁啾%将输入T = mytypes (“fixed16”);b =投(b,'喜欢'、肺结核);x =施法(x,'喜欢', T.x);z = 0(大小(b),'喜欢', T.x);运行%[y, z] = myfilter (b, x, z, T);
类型表
函数T = mytypes (dt)开关dt案件“双”结核病=双([]);T.x =双([]);T.y =双([]);案件“fixed16”肺结核= fi(15)[],真的,16日;T.x = fi(15)[],真的,16日;T.y = fi(14)[],真的,16日;结束结束

表6所示。表4中的测试脚本和过滤算法使用定点数据类型进行了更新。

使用新的固定点类型运行您的算法,并将其输出与基线算法的输出进行比较。

优化数据类型

无论您选择自己的定点数据类型还是使用由fixed point Designer提出的数据类型,都要寻找机会优化单词长度、分数长度、符号,甚至可能是数学模式(fimath).可以通过使用缩放双精度、查看变量值的直方图或在数据类型表中测试不同类型来实现这一点。

使用缩放双倍来检测潜在溢出

按比例缩小的双打是浮点数和定点数的混合。Fixed-Point Designer商店按双倍比例缩放,并保留缩放、符号和字长信息。要使用缩放双精度,请设置数据类型override (DTO)属性(表7)。

DTO设置 例子
本地使用DTO设置numerictype
数据类型的属性
>> T.a = fi([], 1,16,13,“数据类型”'scaleddouble');>> a = cast('喜欢', T.a) a = 3.1416 DataTypeMode:缩放双:二进制点缩放符号:Signed: Signed WordLength: 16 FractionLength: 13
DTO在全局使用fipref
“DataTypeOverride”属性
> > fipref ('datatypeoverride''scaleddoubles');>> T.a = fi([], 1,16, 13);>> a = cast('喜欢', T.a) a = 3.1416 DataTypeMode:缩放双:二进制点缩放符号:Signed: Signed WordLength: 16 FractionLength: 13

表7所示。用于在局部和全局设置数据类型覆盖属性的方法。

使用buildInstrumentedMex运行您的代码和showInstrumentationResults查看结果。在代码生成报告中,溢出的值用红色突出显示(图2)。

图2。代码生成报告显示使用缩放双倍类型和直方图图标时的溢出。
图2。当使用缩放双倍类型(左)和直方图图标(右)时显示溢出的代码生成报告。

检查变量值的分布

您可以使用直方图来识别具有范围内、范围外或精度以下值的数据类型。单击柱状图图标启动numerictyPescope.并查看所选变量模拟中观察到的值的分布(图3)。

图3。显示变量值分布的直方图。
图3。显示变量值分布的直方图,红色表示溢出条件(“超出范围”)。

在数据类型表中测试不同类型

您可以将自己的定点类型变体添加到您的类型表(表8)中。

算法代码
函数[y,z] = myfilter(b,x,z,t)y = zeros(大小(x),'喜欢', T.y);N =1:length(x) z(:) = [x(N)];z (1:结束1)];Y (n) = b * z;结束结束
测试文件
函数mytest%测试输入0.25 b = fir1(11日);t = linspace(0, 10 *π,256);x =罪((π/ 16)* t。^ 2);%线性啁啾运行%y0 =入口点(“双”x、b);日元=入口点(“fixed8”x、b);造成=入口点(“fixed16”x、b);%的阴谋次要情节(1,1);情节(t, x,'C't y0,“k”);传奇(“输入”“基准输出”) 标题(“基线”)次要情节(3 2 3);情节(t,日元,“k”);标题('8位固定点输出')次要情节(3、2、4);情节(t, y0-double(日元)“r”);标题(“8位定点误差”)子图(3,2,5);情节(t,y16,“k”);标题(的16位定点输出)包含(“时间(s)”次要情节(3 2 6);情节(t, y0-double(造成),“r”);标题(的16位定点误差)包含(“时间(s)”结束函数[y,z] =入口点(dt,b,x)t = mytypes(dt);b =投(b,'喜欢'、肺结核);x =施法(x,'喜欢', T.x);z = 0(大小(b),'喜欢', T.x);[y, z] = myfilter (b, x, z, T);结束
类型表
函数T = mytypes (dt)开关dt案件“双”结核病=双([]);T.x =双([]);T.y =双([]);案件“fixed8”肺结核= fi(7)[],真的,8日;T.x = fi(7)[],真的,8日;T.y = fi(6)[],真的,8日;案件“fixed16”肺结核= fi(15)[],真的,16日;T.x = fi(15)[],真的,16日;T.y = fi(14)[],真的,16日;结束结束

表8.测试脚本,用于检查在类型表中使用不同的定点数据类型进行过滤功能的效果。

在每次更改后比较迭代结果以验证算法的准确性(图4)。

图4。表8中测试脚本的图显示了转换为8位和16位定点类型后的输出和错误。
图4。表8中测试脚本的图显示了转换为8位和16位定点类型后的输出和错误。

优化算法

有三种常见的方法可以优化算法,以提高性能并生成更高效的C代码。您可以:

  • 使用fimath属性以提高生成代码的效率
  • 用更有效的定点实现替换内置函数
  • 重新实现部门操作

使用fimath提高生成代码效率的属性

当你使用default时fimath设置时,会生成额外的代码来实现饱和溢出、最近邻舍入和全精度算术(表9a)。

MATLAB代码
代码被编译:函数Y =加法器(a,b)y = a + b;结束使用默认Fimath设置定义的类型:T.a = fi(0)[], 16日;肺结核= fi(0)[], 16日;=投(0,'喜欢', T.a);b =投(0,'喜欢'、肺结核);
生成的C代码
int加法器(短a,短b){int y;int i0;int i1;int i2;int i3;i0 = a;i1 = b;如果{((i0 & 65536) != 0) {i2 = i0 | -65536;}其他的{i2 = i0 & 65535;}如果((I1&65536)!= 0){I3 = I1 |-65536;}其他的{I3 = I1和65535;I0 = I2 + I3;如果((i0 & 65536) != 0) {y = i0 | -65536;}其他的{y = i0 & 65535;}返回y;}

表9a。使用默认Fimath设置生成的原始MATLAB代码和C代码。

要使生成的代码更高效,请选择与处理器类型匹配的定点数学设置。选择fimath属性的数学、舍入和溢出定义在上执行算术运算的规则fi对象(表9 b)。

MATLAB代码
代码被编译:函数Y =加法器(a,b)y = a + b;结束使用与处理器类型匹配的fmath设置定义的类型:f = fimath(...“RoundingMethod”“地板”, ...“OverflowAction”'裹', ...“ProductMode”“KeepLSB”, ...'profectwordlength'32岁的……“SumMode”“KeepLSB”, ...'sumwordlength'、32);T.a = fi ([], 1, 16 0 F);肺结核= fi ([], 1, 16 0 F);=投(0,'喜欢', T.a);b =投(0,'喜欢'、肺结核);
生成的C代码
int加法器(短a,短b){返回a + b;}

表9 b。原始的MATLAB代码和C代码生成与fimath设置匹配的处理器类型。

用定点实现替换内置函数

可以更换一些MATLAB功能以实现更有效的固定点实现。例如,您可以使用查找表实现或替换内置函数CORDIC实现,它只需要迭代的移位加运算。

重新实现部门操作

除法操作通常得不到硬件的完全支持,从而导致处理速度缓慢。金宝app当你的算法需要除法运算时,考虑用更快的方法来替换它。如果分母是2的幂,则使用位移;例如,使用bitsra(x,3)而不是x / 8.如果分母是常数,乘以倒数;例如,使用x * 0.2而不是x / 5

下一个步骤

通过使用固定点设计人员应用这些最佳实践将浮点代码转换为固定点后,需要使用现实的测试输入集来彻底测试您的固定点实现,并将漂浮的钻头真实仿真结果与浮动进行比较- 点基线。

发布于2014 - 92226v00