转换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 = cumulative_sum(x); / /累计金额y = x (1) (1);N =2:长度y(N) = y(N -1) + x(N);结束结束

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

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

使用Fixed-Point Designer,您还可以通过创建MEX文件来加速您的定点算法,并加快根据原始版本验证定点实现所需的模拟。

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

有两种方法可以自动化这一步:

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

一旦您准备好代码生成的算法,您就可以使用fixed point Designer来检测和加速它。使用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:length(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生成的代码生成报告,其中包含过滤算法中变量的建议数据类型。

然后您可以根据需要调整建议的类型(表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}直方图运行%[y, z] = myfilter_mex (b, x, z);%显示showInstrumentationResultsmyfilter_mex……-proposeFL -defaultDT numerictype (16)

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

算法代码
函数[y,z] = myfilter(b,x,z,T)“喜欢”, 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。当使用缩放双倍类型(左)和直方图图标(右)时显示溢出的代码生成报告。

检查变量值的分布

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

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

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

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

算法代码
函数[y,z] = myfilter(b,x,z,T)“喜欢”, 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,造成,“k”);标题(的16位定点输出)包含(“时间(s)”次要情节(3 2 6);情节(t, y0-double(造成),“r”);标题(的16位定点误差)包含(“时间(s)”结束函数[y,z] = entrypoint(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位定点类型后的输出和错误。

优化算法

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

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

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

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

MATLAB代码
代码被编译:函数Y = add (a,b);结束使用默认的fimath设置定义类型:T.a = fi(0)[], 16日;肺结核= fi(0)[], 16日;=投(0,“喜欢”, T.a);b =投(0,“喜欢”、肺结核);
生成的C代码
Int加法器(短a,短b) {Int y;int钱数;int i1;int i2;int i3;i0 =一个;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;}

表9。原始MATLAB代码和C代码生成的默认fmath设置。

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

MATLAB代码
代码被编译:函数Y = add (a,b);结束使用与处理器类型匹配的fmath设置定义的类型:F = fimath(…“RoundingMethod”“地板”,……“OverflowAction”“包装”,……“ProductMode”“KeepLSB”,……“ProductWordLength”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

下一个步骤

在使用fixed point Designer应用这些最佳实践将浮点代码转换为固定点之后,请花时间使用一组真实的测试输入彻底测试您的固定点实现,并将位真模拟结果与您的浮点基线进行比较。

发布于2014 - 92226v00