如何在ARM上添加150个周期的延迟?

如何在ARM上添加150个周期的延迟?,arm,bare-metal,Arm,Bare Metal,根据规范启用和解除时钟信号后,更改Raspberry Pi上GPIO引脚的上拉/下拉电阻器需要等待150个周期。做得再长一点也不会有什么坏处,但用定时器等待的时间要长很多,所以我不想这么做。我有一个简单的忙循环: for (int i = 0; i < 150; ++i) { asm volatile (""); } 0: e3a03096 mov r3, #150 ; 0x96 4: e2533001 subs r3, r

根据规范启用和解除时钟信号后,更改Raspberry Pi上GPIO引脚的上拉/下拉电阻器需要等待150个周期。做得再长一点也不会有什么坏处,但用定时器等待的时间要长很多,所以我不想这么做。我有一个简单的忙循环:

for (int i = 0; i < 150; ++i) { asm volatile (""); }

0:   e3a03096        mov     r3, #150        ; 0x96
4:   e2533001        subs    r3, r3, #1
8:   1afffffd        bne     4 <foo+0x4>
(inti=0;i<150;++i){asm volatile(“”;} 0:e3a03096 mov r3,#150;0x96 4:e2533001接头r3,r3,#1 8:1AFFFFD bne 4 循环150次,执行300条指令。没有指令缓存和分支预测,这肯定超过150个周期。但一旦这些被打开,循环运行的速度就快了,我想比150个循环还要快

那么,无论是否启用指令缓存和分支预测,我如何等待接近150个周期? 注意:最坏的情况可能是2个函数,delay_no_cache()和delay_cache()

这不是的重复,因为指令缓存和分支预测完全抛出了时间。树莓Pi(ARMv6)和树莓Pi2(ARMv7)的计时也不同


如果要在循环中插入DMB、DSB(我想是NOPs,因为没有访问ram)或ISB指令,有人知道执行时间(有缓存和没有缓存)吗?如果启用缓存,是否会防止逃逸效应?

您可能需要使用重复宏功能来执行延迟。在运行时使用循环,总是会有优化,循环本身也会花费时间。您可以迭代NOP宏150次。不会有优化和冗余循环

下面是一个重复的宏模板:

#define MACRO_CMB( A , B)           A##B
#define M_RPT(__N, __macro)         MACRO_CMB(M_RPT, __N)(__macro)

#define M_RPT0(__macro)
#define M_RPT1(__macro)             M_RPT0(__macro)   __macro(0)
#define M_RPT2(__macro)             M_RPT1(__macro)   __macro(1)
#define M_RPT3(__macro)             M_RPT2(__macro)   __macro(2)
...
#define M_RPT256(__macro)           M_RPT255(__macro) __macro(255)
您可以这样定义NOP指令:

#define MY_NOP(__N)                 __asm ("nop");    // or sth like "MOV R0,R0"
然后,只需调用以下命令,即可重复该指令150次:

M_RPT(150, MY_NOP);
它将被执行150次


希望这能有所帮助。

我对Raspberry PI 2 running delay()进行了一些测量,测量了1-100000000个循环的10倍,并根据时间的流逝计算了循环计数。这表明,如果没有缓存,仅通过空循环一次就足以实现150个周期的延迟(即sub+bcs)。单个NOP(150个序列)在没有缓存的情况下需要32个(总共5030个)周期,在没有缓存的情况下需要1.5个周期(总共226.5个)。奥尔;添加及;压敏电阻;奥尔;添加及;压敏电阻;循环还显示了CPU的流水线和超标量,每个操作码只需要0.15个循环。得到一个井次循环不是很好

总之,我不得不放弃,只使用基于计时器的延迟。实际上,没有缓存的循环比使用缓存的循环要快150个周期

void delay(uint32_t count) {
    uint32_t a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0;
    while(count--) {
// branch icache dcache cycles/loop
// no     no     no     ~507
// no     no     yes      43.005
// no     yes    no        1.005
// no     yes    yes       1.005
// yes    no     no     ~507
// yes    no     yes      43.005
// yes    yes    no        1.005
// yes    yes    yes       1.005
// asm ("");

// branch icache dcache cycles/loop
// no     no     no     ~750
// no     no     yes      67.500
// no     yes    no       16.500
// no     yes    yes      16.500
// yes    no     no     ~750
// yes    no     yes      67.500
// yes    yes    no       16.500
// yes    yes    yes      16.500
// asm ("nop");
// asm ("nop");
// asm ("nop");
// asm ("nop");
// asm ("nop");
// asm ("nop");
// asm ("nop");
// asm ("nop");
// asm ("nop");
// asm ("nop");

// branch icache dcache cycles/loop
// no     no     no     ~505
// no     no     yes      43.500
// no     yes    no        1.500
// no     yes    yes       1.500
// yes    no     no     ~505
// yes    no     yes      43.500
// yes    yes    no        1.500
// yes    yes    yes       1.500
 asm ("orr %0, %0, %0" : "=r" (a) : "r" (a));
 asm ("add %0, %0, %0" : "=r" (b) : "r" (b));
 asm ("and %0, %0, %0" : "=r" (c) : "r" (c));
 asm ("mov %0, %0" : "=r" (d) : "r" (d));
 asm ("orr %0, %0, %0" : "=r" (e) : "r" (e));
 asm ("add %0, %0, %0" : "=r" (f) : "r" (f));
 asm ("and %0, %0, %0" : "=r" (g) : "r" (g));
 asm ("mov %0, %0" : "=r" (h) : "r" (h));

// branch icache dcache cycles/loop
// no     no     no     ~1010
// no     no     yes       85.005
// no     yes    no        18.000
// no     yes    yes       18.000
// yes    no     no     ~1010
// yes    no     yes       85.005
// yes    yes    no        18.000
// yes    yes    yes       18.000
// isb();

// branch icache dcache cycles/loop
// no     no     no     ~5075
// no     no     yes      481.501
// no     yes    no       141.000
// no     yes    yes      141.000
// yes    no     no     ~5075
// yes    no     yes      481.501
// yes    yes    no       141.000
// yes    yes    yes      141.000
// isb();
// isb();
// isb();
// isb();
// isb();
// isb();
// isb();
// isb();
// isb();
// isb();
    }
}

可能的重复不是重复。理想情况下,我正在寻找一个具有(几乎)相同指令定时的循环,有指令缓存和分支预测,也有指令缓存和分支预测。我知道上面的简单循环在指令缓存和分支预测的情况下运行速度快了大约500倍,这一点都没有帮助。问题不是优化,因为asm()阻止了优化。这也不是因为循环本身的成本。该成本可以在延迟中考虑,也可以通过展开最小化。问题是150个NOP在没有缓存的情况下需要150个周期,但在有缓存的情况下(或类似情况下)只需要15个周期不使用
%1
,因此,如果编译器碰巧选择了不同的输入/输出寄存器,它可能会使用输出操作数中的任何垃圾作为输入。不过,您已经有了足够的指令级并行性,循环携带的依赖链不会在延迟上出现瓶颈,所以这并不重要。(我对每条指令0.15个周期持怀疑态度。即使是AMD Ryzen,每条时钟也只能管理5条指令(如果有多个uop指令,则为6个uop),和Intel x86 CPU是4宽的超标量。6.66宽是不合理的。哦,我想我知道发生了什么。你没有使用
asm volatile
,你的函数在循环后也没有使用
a、b、c、d、e、f、g、h
,所以gcc优化了你所有的
asm
语句。(至少有一个输出操作数的非易失性asm被认为是其输入的纯函数,没有副作用。)是的,如果您使用
-O1
或更高版本编译,则确认这正是发生的情况。您的函数甚至在x86上编译正常,因为asm会进行优化:P始终检查编译器asm输出。