查看: 1350|回复: 0

LPC800的ROM除法,使用及性能比较

[复制链接]
  • TA的每日心情
    开心
    2024-3-26 15:16
  • 签到天数: 266 天

    [LV.8]以坛为家I

    3300

    主题

    6547

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32032
    最后登录
    2024-4-26
    发表于 2019-6-25 09:18:47 | 显示全部楼层 |阅读模式
    一、背景


    与Cortex-M3和M4不同,Cortex-M0/M0+不支持硬件除法,这意味着当应用程序执行除法运算时,编译器将调用C库中的除法函数进行运算。 因此,如果用户的应用程序或它调用的库中使用了除法运算,则这些被用到C库中的除法函数将会消耗一部分Flash中的空间。


    在LPC800系列产品中,Flash最大为32KB, 由于Flash大小的限制,使得Flash中的每一块空间都显得弥足珍贵。为了减少C库中除法对Flash空间的消耗,LPC800的ROM中包含了一些优化的除法代码用来执行除法运算,若用户使用了对应的除法代码,则无须使用C库中的除法函数了,此时链接器便不会将C库中除法函数链接到工程中,从而实现节省Flash空间的作用。


    ROM中的除法代码以“接近恒定时间”运行,不管输入和结果如何,特定的除法消耗的周期是大致相同的;而C库中除法的运算时间是不固定的,和商的位数有关。


    ROM除法的运算时间可能比C库中除法的最快性能慢,但要快于C库除法的最慢情况。


    二、ROM除法的使用


    2.1 在Keil中使用ROM除法函数


    1,直接调用ROM除法API函数


    在LPC800系列产品中,都提供了ROM除法的API 接口,包括优化的有符号和无符号整数除法,及带余数的有符号和无符号整数除法,用户可以直接通过这些API接口调用这些除法函数。


    下面是ROM中函数的排列方式。在0x0F00-1FF8处是ROM函数表的起始地址,ROM函数表中的第五项(偏移位置是0x10)是除法函数的入口地址表。
    1.png


    下面是LPC800用户手册中关于ROM除法函数的原型说明,这里包含了四个函数。
    2.png
    直接调用这些函数的使用方法如下:
    3.png
    2,重载“/”和“%”运算符


    LPC804的Code Boundle Rom divide工程中提供了一个rom_div_84x.c文件,文件中的代码实现了将“/”和“%”运算符重载到ROM除法API函数上。


    LPC804 Code Boundle可以在恩智浦的官网上下载,下载链接为http://www.nxp.com/downloads/en ... 04-EX-CODE-KEIL.zip


    使用时,用户只需将rom_div_84x.c文件添加到自己的工程中,即可实现“/”和“%”运算符的重载。此时我们就可以通过使用“/”和“%”运算符来实现调用ROM的除法函数。


    例如执行下面两个语句的结果是相同的:


    res1 = I32Dividend / I32Divisor;



    res1 = pROMDiv->sidiv(I32Dividend, I32Divisor);
    4.png
    2.2 在MCUXpresso IDE中使用ROM的除法函数


    1,直接调用ROM的除法API函数


    在MCUXpresso IDE中直接调用ROM的除法API函数,方法和在Keil中是相同的,具体方法请参考前面2.1节。


    2,重载”/” 和 “%” 运算符


    在MCUXpresso IDE中重载“/”和“%”操作符的方法和在Keil中的方法是不同的,具体操作方法如下:


    ① 将"MCUXpresso_aeabi_romdiv_patch.s"文件添加到自己工程路径中;


    ② 选择Properties->C/C++ Build->Settings->MCU C Compiler/Preprocessor,添加“__USE_ROMDIVIDE”宏定义;

    5.png
    ③ 选择Properties->C/C++ Build->Settings->MCU Assembler/General,添加“-D__USE_ROMDIVIDE”。


    完成这三步之后,“/”和“%”运算符就已经被重载到ROM的除法函数上。


    三、ROM除法与C库的除法对比


    为了比较ROM的除法和C库除法性能的差异,选取一组数据进行测试,执行有符号的整数计算和带余数的无符号整数计算,分别使用直接调用ROM除法API、重载“/”和“%”运算符及C库的除法三种方法进行计算,每种方式计算2000次,并分别在Keil 5.25和MCUXpresso 10.2.0两种IDE中进行了计算。选取的数据如下:


    volatile const int32_t I32Dividends[10] =  {-65530, -12002, -64498,  30883,  41538,  23513,  12972, 53852,  10253, 37127};

    volatile const int32_t I32Divisors[10] =   {-65525, -50973, -36407, -21845, - 7281,   7281,  21845, 36407,  50971, 65535};

    volatile const uint32_t U32Dividends[10] = { 37480,   4630,  59349,  20616,    727,  29784,  56556, 53166,  35237, 41271};

    volatile const uint32_t U32Divisors[10] =  {     1,   7283,  14563,  21845,  29127,  36407,  43691, 50973,  58253, , 65535};


    3.1 Keil中的测试结果对比


    1,除法运算时间的对比



    在Keil中使用三种方法的测试结果如下表所示:
    6.png
    注:上表所有时间单位是ms,下同。


    从表中可以看出,在进行有符号整数计算(sidiv)时,直接调用ROM除法API的方式要慢于使用C库除法的方式;但在进行无符号带余数的整数计算(uidivmod)时,直接调用的ROM除法API的方式要快于C库中的除法,在优化等级为O0时,比C库除法快约20.44%。



    在使用运算符重载的方式计算两种除法时,其运算时间都要大于C库除法所用时间,主要原因在于重载时,执行了一些参数入栈和出栈操作,消耗了一些时间,如下图所示:
    7.png
    2,Flash使用率的对比



    使用ROM除法和C库除法时的Flash使用量如下表所示:
    8.png
    从上表中可以看出,使用ROM除法可以减小Flash空间的使用,大约节省了300字节的Flash空间。这主要是因为使用ROM除法时,链接器没有将C库中的aeabi_sdiv.o文件链接到工程中。在Keil中,aeabi_sdiv.o文件占用大小346字节。
    9.png
    3.2 MCUXpresso 10.2.0 中测试结果的对比


    1,除法运算时间的对比



    在MCUXpresso 10.2.0中使用三种方法的测试结果如下表所示:
    10.png
    在MCUXpresso中的测试结果与Keil中的测试结果是相似的,在进行有符号的整数计算时,直接调用ROM除法API的方式要慢于C库除法;在进行无符号带余数的整数计算时,直接调用ROM除法API的方式要快于C库除法,在优化等级为00时,比C库除法快约23.76%。


    在使用运算符重载的方式计算两种除法时,其运算时间都要大于C库除法所用时间。因此在对除法运算时间有要求的情况下,推荐使用直接调用ROM除法API的方式。


    2,Flash使用率的对比



    使用ROM除法和C库除法时的Flash使用量如下表所示:
    11.png
    从表中可以看出,使用ROM除法减小了Flash空间的使用,在MCUXpresso IDE中使用ROM除法节省了约90字节的Flash空间。


    3.3 测试方法



    在测试工程中设置每种除法计算完成之后,翻转一下GPIO口的电平,使用逻辑分析仪测量各GPIO口的高电平时间,如下图所示:
    12.png
    部分测试代码如下:

    LPC_GPIO_PORT->SET[0] = 1<<20;
    for (i=0; i<200; i++)
    {
        for (k=0; k!=10; k++)
            direct_i32[k] = pROMDiv->sidiv(I32Dividends[k], I32Divisors[k]);
    }
    LPC_GPIO_PORT->CLR[0] = 1<<20;
    LPC_GPIO_PORT->SET[0] = 1<<13;for (i=0; i<200; i++)
    {
        for (k=0; k!=10; k++)
            overload_i32[k] = (I32Dividends[k] / I32Divisors[k]);
    }
    LPC_GPIO_PORT->CLR[0] = 1<<13;
    LPC_GPIO_PORT->SET[0] = 1<<21;
    for (i=0; i<200; i++)
    {
        for (k=0; k!=10; k++)
            direct_uidivmod[k] = pROMDiv->uidivmod(U32Dividends[k], U32Divisors[k]);
    }
    LPC_GPIO_PORT->CLR[0] = 1<<21;
    LPC_GPIO_PORT->SET[0] = 1<<15;
    for (i=0; i<200; i++)
    {
        for (k=0; k!=10; k++) {
            div_val[k] = (U32Dividends[k] / U32Divisors[k]);
            mod_val[k]= (U32Dividends[k] % U32Divisors[k]);
        }
    }

    LPC_GPIO_PORT->CLR[0] = 1<<15;

    四、总结


    从以上的对比结果中我们可以看出,使用ROM除法代替C库除法的方式,可以在一定程度上帮助用户节省Flash空间,这对LPC800系列产品是很有意义的。



    此外ROM除法的运算时间也是要快于C库除法的最慢时间,在计算无符号带有余数的除法运算时,ROM除法的计算时间比C库除法快约20%,如用户对除法的运算时间有较高要求,也推荐使用直接调用ROM除法的方式进行计算。
    LPC800系列往期内容回顾



    作者:张杨   文章出处:恩智浦MCU加油站



    签到签到
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-26 22:50 , Processed in 0.120558 second(s), 20 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表