代码之家  ›  专栏  ›  技术社区  ›  diwhyyyyy

在带有AVR gcc的AVR XMEGA上的定时循环中不满足内联汇编程序约束

  •  2
  • diwhyyyyy  · 技术社区  · 11 年前

    我在AVR目标C中遇到毫秒延迟环路问题。我正在使用 avr-gcc 4.7.0 在Linux上,我从Ubuntu repos获得了它,我也尝试了一个新编译的4.7.2。目标硬件是XMEGA128A1,目前时钟频率为2 MHz。

    以下函数经常(但并非总是如此——在遇到问题之前,我曾对该函数进行过几次调用)抱怨这些约束:

    ../common/spin_delay.h:64:9: warning: asm operand 3 probably doesn’t match constraints [enabled by default]
    ../common/spin_delay.h:64:9: error: impossible constraint in ‘asm’
    

    如果我删除了因此错误而停止编译的特定调用,它还会抱怨操作数0、1和2。

    #define LOOPS_PER_MS ((CPU_CLK_HZ/1000)/4)
    
    static inline void ms_spin(unsigned short ms) {
        if (ms) {
            __asm__ __volatile__ (
                "   ldi r24, %1     \n"
                "   ldi r25, %0     \n"
                "1: ldi r26, %3     \n"
                "    ldi r27, %2    \n"
                "2: sbiw r26, 1     \n"
                "    brne 2b        \n"
                "    sbiw r24, 1    \n"
                "    brne 1b        \n"
                : 
                : "M" (ms >> 8), 
                  "M" (ms & 0xff),
                  "M" (LOOPS_PER_MS >> 8), 
                  "M" (LOOPS_PER_MS & 0xff)
                : "r24", "r25", "r26", "r27"
            );
        }
    }
    

    相同的代码在Windows上编译得很好 avr-gcc 4.3.3 然而,从WinAVR开始,这让我认为从那时起,内联汇编程序发生了一些变化。

    在我看来,这一切看起来都是正确的,因为16位短被分解为高字节和低字节,并受到“M”(8位常数)的约束,而它工作了几次的事实排除了硬定义的问题 CPU_CLK_HZ -导出的常数。问题操作数是0-3中的任何一个,这意味着它不是一个特定的asm操作数。

    我也尝试过使用 n 建议的约束 here ,但错误仍然存在。

    1 回复  |  直到 11 年前
        1
  •  2
  •   JimmyB    10 年前

    编辑:

    最近的gcc版本支持内置的周期精确延迟:

    void __builtin_avr_delay_cycles (unsigned long ticks)

    (不过请注意:“ticks必须是编译时整数常量;不支持循环数可变的延迟。”)


    我认为问题在于你对 ms 变量作为 常数 输入到汇编程序。我认为,这甚至不应该在一开始就编译。

    如果它真的进行了编译,那可能只是因为编译器优化使gcc“识别”声明为变量的内容( 太太 )实际上是常量,并且在编译时已知。然而,不能指望这些优化总是以同样的方式发生。

    我的建议是这样的 使用 "M" 的约束 太太 ,因为这里基本上不合适。

    通过使用“正确”的约束,您可能会得到更健壮、更正确的代码,甚至可能更具性能,如:

    #define LOOPS_PER_MS (((CPU_CLK_HZ/1000)-(1+1+2+2))/(2+2)) // accounting for the overhead of 6 (1+1+2+2) cycles per ms and the 4 (2+2) cycles per inner loop iteration
    
    static inline void ms_spin(unsigned short ms) {
        if (ms) {
            unsigned short dummy;
            __asm__ __volatile__ (
                "ms_spin_outer_loop_%=:                \n"
    
                "    ldi %A[loopcnt], lo8(%[loops])    \n"
                "    ldi %B[loopcnt], hi8(%[loops])    \n"
    
                "ms_spin_inner_loop_%=:                \n"
    
                "    sbiw %A[loopcnt], 1               \n"
                "    brne ms_spin_inner_loop_%=        \n"
    
                "    sbiw %A[ms], 1                    \n"
                "    brne ms_spin_outer_loop_%=        \n"
    
                :  [ms] "+w" (ms),
                   [loopcnt] "=&w" (dummy)
                :  [loops] "i" (LOOPS_PER_MS)
                :  // none.
            );
        }
    }
    

    使用“正确”约束还将要使用的特定寄存器集的选择留给gcc,这可能会导致围绕内联汇编语句进行更好的优化。

    为了使事情更加可预测,您可能需要考虑强制gcc 总是 通过 __attribute__((always_inline)) 装饰

    有关avr-gcc中内联汇编程序约束的更多详细信息,请参见 avr-libc documentation 。此列表并非详尽无遗,因此请查看 gcc documentation 可能会不时被证明是有帮助的。