更改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>
循环150次,执行300条指令。没有指令缓存,没有分支预测肯定超过150个周期。但是一旦打开这个循环,那个循环就会比我认为的150个循环更快,更快。
那么在启用或不启用指令缓存和分支预测的情况下,如何等待接近150个周期? 注意:最坏的情况可能是2个函数,delay_no_cache()和delay_cache()
这不是How to delay an ARM Cortex M0+ for n cycles, without a timer?的重复,因为指令缓存和分支预测完全抛出了时序。 Raspberry Pi(ARMv6)和Raspberry Pi2(ARMv7)之间的时序也不同。
是否有人知道执行时间(有和没有缓存),如果有人插入DMB,DSB(我猜那些将是NOP,因为没有访问ram)或ISB指令进入循环?这会阻止缓存启用时的失控效应吗?
答案 0 :(得分:1)
我对Raspberry PI 2运行延迟()进行了一些测量,测量了因子为10的1-100000000次循环,并计算了从经过时间开始的循环次数。这表明没有缓存只需要通过一个空循环就足以实现150个循环的延迟(这只是sub + bcs)。单个NOP(序列为150)总共需要32个(总共5030个)循环而没有高速缓存,1.5个循环(总共226.5个)。 orr; add;和; mov; orr; add;和; mov;循环还显示了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();
}
}
答案 1 :(得分:0)
您可能需要使用 REPEAT MACRO FUNCTION 来延迟。在运行时使用循环,总会有优化,循环本身也需要时间。您可以迭代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次。
希望这会有所帮助。