PIC32速度:优化c代码

时间:2015-11-01 17:07:27

标签: c assembly embedded pic32

我想要一些建议来优化我的代码,这是一个简单的代码,但它需要快速,快速我的意思是小于250 ns。
我的第一个代码很慢,大约1000 ns,但有些工作后大约550 ns 但我相信它可以更快地完成,但我不知道如何:<
 我使用的是具有80 MHz系统时钟的PIC32 我的代码:

void main()
{
    unsigned long int arr_1[4095]; 
    unsigned long int arr_2[4095]; 

    //here I assign arr_1 and arr_2 values
    //...
    //...

    TRISC = 0;
    TRISD = 0;

    while(1){
         LATC = arr_1[PORTE];
         LATD = arr_2[PORTE];
    }

}

因为你可以看到它作为一个工作非常简单,唯一的问题是速度 我看到汇编列表只是为了看看有多少指令,但我不知道汇编语言来优化它。

;main.c, 14 ::      LATC = arr_1[PORTE];
0x9D000064  0x27A30000  ADDIU   R3, SP, 0
0x9D000068  0x3C1EBF88  LUI R30, 49032
0x9D00006C  0x8FC26110  LW  R2, 24848(R30)
0x9D000070  0x00021080  SLL R2, R2, 2
0x9D000074  0x00621021  ADDU    R2, R3, R2
0x9D000078  0x8C420000  LW  R2, 0(R2)
0x9D00007C  0x3C1EBF88  LUI R30, 49032
0x9D000080  0xAFC260A0  SW  R2, 24736(R30)
;main.c, 15 ::      LATD = arr_2[PORTE];
0x9D000084  0x27A33FFC  ADDIU   R3, SP, 16380
0x9D000088  0x3C1EBF88  LUI R30, 49032
0x9D00008C  0x8FC26110  LW  R2, 24848(R30)
0x9D000090  0x00021080  SLL R2, R2, 2
0x9D000094  0x00621021  ADDU    R2, R3, R2
0x9D000098  0x8C420000  LW  R2, 0(R2)
0x9D00009C  0x3C1EBF88  LUI R30, 49032
;main.c, 16 ::      }
0x9D0000A0  0x0B400019  J   L_main0
0x9D0000A4  0xAFC260E0  SW  R2, 24800(R30)  

有关优化代码的建议吗?

编辑:
* PORTE,LATC和LATD是I / O映射寄存器 *当PORTE改变时,代码的目标是尽快更改LATC和LATD寄存器(因此PORTE是输入,输出LATC和LATD),输出取决于PORTE的值

1 个答案:

答案 0 :(得分:1)

一个潜在的限制因素是,由于PORTELATCLATD不是常规内存而是I / O寄存器,因此I / O总线速度可能较低比内存总线速度和处理器在访问之间插入等待状态。对于PIC32,情况可能是也可能不是,但对于任何体系结构,您都需要考虑这一点。

如果I / O总线不是限制,那么首先应用编译器优化吗?对于这种微优化通常是您最好的选择。这段代码似乎很容易优化,但是汇编程序似乎没有反映出来(虽然我不是MIPS汇编程序专家 - 但编译器优化器却是这样)。

由于I / O寄存器是易失性的,因此优化器可能会在优化循环体时失败。但由于它们是易变的,因此代码可能也是不安全的,因为PORTE可能(实际上可能)在LATCLATD的分配之间改变值是你的意图或期望。如果是这种情况,则代码应更改如下:

int porte_value_latch = 0 ;
for(;;)
{
     // Get a non-volatile copy of PORTE.
     porte_value_latch = PORTE ;  

     // Write LATC/D with a consistent PORTE value that 
     // won't change between assignments, and does not need 
     // to be read from memory or I/O.
     LATC = arr_1[porte_value_latch] ;
     LATD = arr_2[porte_value_latch] ;
}

然后安全且可能更快,因为volatile PORTE只读取一次,并且porte_value_latch值可以保留在临时寄存器中以进行两次数组访问,而不是每次都从内存中读取。即使常规编译没有,优化器也几乎肯定会将其优化为寄存器访问。

for(;;)而不是while(1)的使用可能没什么区别,但是有些编译器会对表达式发出不变的警告,bit会静静地接受for(;;)成语。您没有为第13行包含代码汇编程序,因此无法确定编译器生成的内容。

如果LATCLATD位于相邻地址,则可以使用进一步优化的可能性,在这种情况下,您可以使用类型unsigned long long int的单个数组来编写单个作业中的位置。当然,64位访问仍然是非原子的,但编译器在任何情况下都可以生成更高效的代码。它还巧妙地避免了porte_value_latch变量的需要,因为只有一个PORTE的引用。但是,如果必须按特定顺序编写LATCLATD,则会失去该控制级别。循环看起来像:

for(;;)
{
    LATCD = arr_1_2[PORTE] ;
}

LATCD的地址是相邻LATCLATD寄存器的低位地址,类型为unsigned long long int。如果LATC的地址较低,则为:

unsigned long long int LATCD = (unsigned long long int)LATC ;

因此写入LATCD会写入LATC和LATD。然后,玩具必须将arr_1arr_2合并为一个unsigned long long数组,并使用适当的字序,以便它在单个值中包含C和D值。

另一个建议:配置硬件使用从> = 4MHz的时钟信号触发的DMA将PORTE读取到单个位置。然后,环路根本不需要读取PORTE,而是读取DMA存储器位置,该位置可能更快,也可能不更快。您还可以设置DMA以从内存位置写入LATC / LATD,以便循环根本不执行I / O.该方法还允许相邻的存储器"即使LATC和LATD实际上不相邻也能工作的方法。

最终如果问题仅限于编译器的代码生成,那么在内联汇编器中实现循环并手动优化它可能是有意义的。