我有一个用C语言编写的带有Xilinx Microblaze内核的内联汇编的应用程序。我的内联程序集有一个延迟任务。函数“ _delay_loop_X_x”每个处理器循环正好延迟4个周期。输入信号确定要进行的循环次数。函数“ _NOPx”是为了达到更高的精度。该功能可以正常工作,但是在信号结束时它会提供两倍的额外延迟。恐怕我使用寄存器不正确。有人可以检查我的汇编代码吗?对于Microblaze,我使用以下文档:https://www.xilinx.com/support/documentation/sw_manuals/mb_ref_guide.pdf汇编代码: static __inline__ void _delay_loop_1_x( uint8_t) __attribute__((always_inline)); static __inline__ void _NOP1 (void) {__asm__ volatile ("nop \n\t" ); } //1 cycle static __inline__ void _NOP2 (void) {__asm__ volatile ("beqi r12, 1f \n\t""1:\n\t" ::: "r12", "cc" ); } //2 cycle static __inline__ void _NOP3 (void) {__asm__ volatile ("brk r12, r0 \n\t" ::: "r12", "cc" ); } //3 cycle static __inline__ void /* exactly 4 cycles */ _delay_loop_1_x( uint8_t __n ) { /* cycles per loop */ __asm__ volatile ( " addik r11, r0, 1 \n\t" /* 1 */ "1: rsub %[input], r11, %[input] \n\t" /* 1 */ " beqi %[input], 2f \n\t" /* 1 */ "2: bnei %[input], 1b \n\t" /* 1 */ : /* ----- */ : [input]"r" (__n) /* ----- */ : "r11", "cc" /* 4 */ ); } static __inline__ void /* exactly 4 cycles/loop */ _delay_loop_2_x( uint16_t __n ) { /* cycles per loop */ __asm__ volatile ( /* __n..one */ " addik r11, r0, 1 \n\t" /* 1 */ "1: rsub %[loops], r11, %[loops] \n\t" /* 1 */ " beqi %[loops], 2f \n\t" /* 1 */ "2: bnei %[loops], 1b \n\t" /* 1 */ : /* ----- */ : [loops]"r" (__n) /* ----- */ : "r11", "cc" /* 4 */ ); } static __inline__ void _delay_cycles(const double __ticks_d) { uint32_t __ticks = (uint32_t)(__ticks_d); uint32_t __padding; uint32_t __loops; if( __ticks <= 3 ) { __padding = __ticks; } else if( __ticks <= 0x400 ) { __ticks -= 1; __loops = __ticks / 4; __padding = __ticks % 4; if( __loops != 0 ) _delay_loop_1_x( (uint8_t)__loops ); } else if( __ticks <= 0x40001 ) { __ticks -= 2; __loops = __ticks / 4; __padding = __ticks % 4; if( __loops != 0 ) _delay_loop_2_x( (uint16_t)__loops ); } if( __padding == 1 ) _NOP1(); if( __padding == 2 ) _NOP2(); if( __padding == 3 ) _NOP3(); }C代码: #define _delay_ns(__ns) _delay_cycles( (double)(F_CPU)*((double)__ns)/1.0e9 + 0.5 ) #define _delay_us(__us) _delay_cycles( (double)(F_CPU)*((double)__us)/1.0e6 + 0.5 ) #define _delay_ms(__ms) _delay_cycles( (double)(F_CPU)*((double)__ms)/1.0e3 + 0.5 ) #define BIT_DELAY_1 _delay_ns(2070) #define BIT_DELAY_5 _delay_us(19) #define BIT_DELAY_7 _delay_us(26) #define RX_TX_DELAY _delay_us(78) #define SHA204_SWI_FLAG_TX ((uint8_t) 0x88) XGpio GpioPIN; uint8_t swi_send_bytes(uint8_t count, uint8_t *buffer); uint8_t swi_send_byte(uint8_t value); int main() { init_platform(); XGpio_Initialize(&GpioPIN, GPIO_PIN_DEVICE_ID); XGpio_SetDataDirection(&GpioPIN, PIN_CHANNEL, ~PIN); (void) swi_send_byte(SHA204_SWI_FLAG_TX); cleanup_platform(); return 0; } uint8_t swi_send_byte(uint8_t value) { return swi_send_bytes(1, &value); } uint8_t swi_send_bytes(uint8_t count, uint8_t *buffer) { uint8_t i, bit_mask; RX_TX_DELAY; for (i = 0; i < count; i++) { for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1) { if (bit_mask & buffer[i]) { XGpio_DiscreteClear(&GpioPIN, PIN_CHANNEL, PIN); BIT_DELAY_1; XGpio_DiscreteWrite(&GpioPIN, PIN_CHANNEL, PIN); BIT_DELAY_7; } else { XGpio_DiscreteClear(&GpioPIN, PIN_CHANNEL, PIN); BIT_DELAY_1; XGpio_DiscreteWrite(&GpioPIN, PIN_CHANNEL, PIN); BIT_DELAY_1; XGpio_DiscreteClear(&GpioPIN, PIN_CHANNEL, PIN); BIT_DELAY_1; XGpio_DiscreteWrite(&GpioPIN, PIN_CHANNEL, PIN); BIT_DELAY_5; } } } return 0; }我的结果:https://imgur.com/a/beBgn 最佳答案 多考虑一下,我真的看不到那些循环应该是Nx4周期。static __inline__ void /* exactly 4 cycles */_delay_loop_1_x( uint8_t __n ){ /* cycles per loop */ __asm__ volatile ( " addik r11, r0, 1 \n\t" /* 1 */ "1: rsub %[input], r11, %[input] \n\t" /* 1 */ " beqi %[input], 2f \n\t" /* 1 */ "2: bnei %[input], 1b \n\t" /* 1 */ : /* ----- */ : [input]"r" (__n) /* ----- */ : "r11", "cc" /* 4 */ );}一旦C部分结束(函数的序言)并且代码将开始执行ASM部分,我将看到:n = 1(假设编译器将r1用于input):我猜想执行的指令(逐步)将是这样的:addik r11, r0, 1 ; r11 = 1 (r0 == fixed zero, right?)rsub r1, r11, r1 ; r1 = r1 - r11 (i.e. r1 = 0 in this example)beqi r1, 2f ; r1 is zero, so branch to "2" will be takenbnei r1, 1b ; r1 == 0, branch not taken然后,将遵循C剩下的所有指令(功能说明)。如果每个指令是1个周期(有很多依赖性,那么在高频现代CPU上是不太可能的,但是如果microblaze是没有多级流水线的低频简单RISC架构,那么它可能会那样工作),那么您有4个周期。对于n = 2addik r11, r0, 1 ; r11 = 1rsub r1, r11, r1 ; r1 = 1beqi r1, 2f ; r1 != 0, branch not takenbnei r1, 1b ; r1 != 0, branch takenrsub r1, r11, r1 ; r1 = 0beqi r1, 2f ; r1 == 0, branch taken to bneibnei r1, 1b ; r1 == 0, branch not taken那是7条指令,而不是8条指令。对于8条指令,您需要将bnei设置为addik以便再次将r11重新设置为1以进行延迟(即使该值已在r11中设置)。无论如何,这让我想知道该CPU的时序是否真的那么简单(即使在分支时,一条指令= 1个周期),以及为什么不将循环简化为简单的倒数(对于):1: raddik %[input], %[input], -1 bnei %[input], 1b然后,您将获得2个周期的延迟循环。但是使用延迟的主要代码...具有大量的C代码,这些代码也将转换为机器代码指令,并且这些代码也需要一些时间才能执行,因此不清楚您要测量的内容,原因以及是否确实考虑了由这些附加指令引起的执行延迟。更新关于input崩溃..不知道,没有道理,因此您必须进入调试器并在计算机级别上找出真正的原因。但是整个事情没有多大意义,因为您尝试用C表达式(如ticks -= 1的块)分割循环延迟参数ticks_d。而且这些条件测试将比实际填充值花费更多的周期,因此没有必要调用if (padding == ...)进一步延迟,因为您已经延迟了几十个周期。现在我也终于发现,NOPx(); Olaf提到,仅此一个(对于剩余的数学转换为整数)当然会导致巨大的性能损失,甚至可能在数百个周期内。因此,整个double实际上会比您预期的延迟更多的周期,ASM部分在总时间中可以忽略不计(除了内部循环每个参数值消耗大约3个周期之外,这可能会增加总延迟)如果最初的论点足够大的话)。如果您确实需要指令周期的准确性(不那么普遍,我上次在1990年左右才需要在8位计算机上使用它),则必须在纯asm中不使用C编写它,并将准备/逻辑指令也算入延迟中-理想情况下以固定执行时间的方式编写,因此调用void delay_cycles(const double ticks_d)函数将为任何delay_cycles参数值产生固定的开销。然后,您可以计算该固定的开销,并首先执行ticks_d以便稍后具有确切的延迟。而且您必须从ticks -= <overhead>;切换为整数,因为Microblaze CPU没有FPU单元,因此浮点值是由整数数学模拟的,仅初始转换为int double必须花费一些周期。如果您具有反汇编视图并通过一条指令进入该视图,则在调试器中执行该操作。
08-15 23:38