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"
        );
    }
}

avr-gcc 4.3.3ただし、WinAVR を使用して Windows で同じコードを問題なくコンパイルできることから、それ以降、インライン アセンブラに何らかの変更が加えられたと思われます。

私の考えでは、16ビットのショートは上位バイトと下位バイトに分解され、「M」(8ビット定数)によって制約されるため、すべて正しいように見えますが、数回機能したという事実は問題を除外しますハード定義されたCPU_CLK_HZ派生定数。問題のオペランドが 0 から 3 のいずれかであるという事実は、失敗しているのは特定の asm オペランドではないことを意味します。

ここnで推奨されている制約も使用しようとしましたが、エラーは解決しませんでした。

4

1 に答える 1

2

編集:

最近の gcc バージョンは、サイクルに正確な遅延の組み込みをサポートしています。

void __builtin_avr_delay_cycles (unsigned long ticks)

(ただし、「ティックはコンパイル時の整数定数でなければなりません。可変サイクル数の遅延はサポートされていません。」)


アセンブラへの定数ms入力としての変数の宣言に問題があると思います。私は、これはそもそもコンパイルするべきではないと信じています。

コンパイルする場合、それはおそらく、変数として宣言されているもの(ms)が実際には定数であり、コンパイル時に既知であることをgccに「認識」させるコンパイラの最適化のためだけです。ただし、これらの最適化が常に同じ方法で行われるとは限りません。

したがって、ここでは基本的に場違いなので、制約を使用しないことをお勧めします。"M"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 に答える