2

我在针对 AVR 的 C 中的毫秒延迟循环有问题。我avr-gcc 4.7.0在从 Ubuntu 存储库获得的 Linux 上使用,并且我还尝试了新编译的 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 上使用 WinAVR 编译得很好avr-gcc 4.3.3,这让我认为这是从那时起内联汇编器的一些变化。

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

我也尝试使用这里n推荐的约束,但错误仍然存​​在。

4

1 回答 1

2

编辑:

最近的 gcc 版本支持内置的循环精确延迟:

void __builtin_avr_delay_cycles (unsigned long ticks)

(但请注意:“ticks 必须是编译时间整数常量;不支持具有可变周期数的延迟。”)


我认为问题在于您将ms变量声明为汇编程序的常量输入。我相信,这甚至不应该首先编译。

如果并且当它编译时,可能只是因为编译器优化使 gcc “识别”,声明为变量 ( ms) 的内容实际上是常量并且在编译时是已知的。然而,不能依赖这些优化总是以相同的方式发生。

因此,我的建议是不要使用"M"约束 for ms,因为它在这里基本上是不合适的。

通过使用“正确”约束,您可能会获得更健壮和正确的代码,甚至可能更高性能,如下所示:

#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 文档。此列表并不详尽,因此查看一般gcc 文档可能会不时提供帮助。

于 2013-03-04T15:47:34.447 回答