5

私は現在、Atmel AVR マイクロコントローラー (gcc) を使用していますが、一般的なマイクロコントローラーの世界、つまり通常はシングル スレッドであるが割り込みを伴うものに回答を適用したいと考えています。

volatileISR で変更できる変数にアクセスするときに C コードで使用する方法を知っています。例えば:

uint8_t g_pushIndex = 0;
volatile uint8_t g_popIndex = 0;
uint8_t g_values[QUEUE_SIZE];

void waitForEmptyQueue()
{
    bool isQueueEmpty = false;
    while (!isQueueEmpty)
    {
        // Disable interrupts to ensure atomic access.
        cli();
        isQueueEmpty = (g_pushIndex == g_popIndex);
        sei();
    }
}

ISR(USART_UDRE_vect) // some interrupt routine
{
    // Interrupts are disabled here.
    if (g_pushIndex == g_popIndex)
    {
        usart::stopTransfer();
    }
    else
    {
        uint8_t value = g_values[g_popIndex++];
        g_popIndex &= MASK;
        usart::transmit(value);
    }
}

g_popIndex は ISR の内部で変更され、ISR の外部でvolatileアクセスされるため、その変数へのメモリ アクセスを最適化しないようにコンパイラに指示するように宣言する必要があります。ISR によって変更されていないため、私が間違っていない限り、宣言する必要がないことに注意してg_pushIndexくださいg_valuesvolatile

クラス内のキューに関連するコードをカプセル化して、再利用できるようにしたいと考えています。

class Queue
{
public:
    Queue()
    : m_pushIndex(0)
    , m_popIndex(0)
    {

    }

    inline bool isEmpty() const
    {
        return (m_pushIndex == m_popIndex);
    }

    inline uint8_t pop()
    {
        uint8_t value = m_values[m_popIndex++];
        m_popIndex &= MASK;
        return value;
    }

    // other useful functions here...

private:
    uint8_t m_pushIndex;
    uint8_t m_popIndex;
    uint8_t m_values[QUEUE_SIZE];
};

Queue g_queue;

void waitForEmptyQueue()
{
    bool isQueueEmpty = false;
    while (!isQueueEmpty)
    {
        // Disable interrupts to ensure atomic access.
        cli();
        isQueueEmpty = g_queue.isEmpty();
        sei();
    }
}

ISR(USART_UDRE_vect) // some interrupt routine
{
    // Interrupts are disabled here.
    if (g_queue.isEmpty())
    {
        usart::stopTransfer();
    }
    else
    {
        usart::transmit(g_queue.pop());
    }
}

上記のコードは間違いなくより読みやすいです。volatileとありますが、この場合どうすればよいのでしょうか。

1) それはまだ必要ですか? メソッドを呼び出すと、関数が宣言されている場合でも、Queue::isEmpty()への最適化されていないアクセスが保証されますか? 私はそれを疑います。コンパイラがヒューリスティックを使用してアクセスを最適化する必要がないかどうかを判断することは知っていますが、一般的な解決策としてそのようなヒューリスティックに頼るのは嫌いです。g_queue.m_popIndexinline

Queue::m_popIndex volatile2)機能する(そして効率的な)解決策は、クラス定義内でメンバーを宣言することだと思います。ただし、クラスの設計者は、Queueどのメンバー変数がvolatile. 将来のコード変更にうまく対応できません。また、一部のインスタンスがISR 内で使用されていない場合でも、すべてのQueueインスタンスがメンバーを持つようになりました。volatile

3) クラスをビルトインのように見る場合、ISR で変更され、ISR の外部でアクセスされるため、グローバル インスタンス自体を としてQueue宣言するのが自然な解決策だと思います。ただし、オブジェクトに対して呼び出すことができるのは関数のみであるため、これはうまく機能しません。突然、すべてのメンバー関数を宣言する必要があります( ISR 内で使用されるものだけでなく)。繰り返しますが、デザイナーはどのようにしてそれを事前に知ることができますか? また、これはすべてのユーザーにペナルティを課します。すべてのメンバー関数を複製し、クラスにと non-の両方のオーバーロードを含める可能性がまだあるため、非ユーザーはペナルティを受けません。かわいくない。g_queuevolatilevolatilevolatileQueuevolatileconstQueueQueuevolatilevolatilevolatile

4)クラスは、必要に応じてすべてのメンバー変数にQueueオプションで追加できるポリシー クラスでテンプレート化できます。volatile繰り返しますが、クラス設計者は事前にそのことを知っておく必要があり、解決策は理解するのがより複雑になりますが、まあまあです。

これに対する簡単な解決策が欠けているかどうか知りたいです。ちなみに、私は (まだ) C++11/14 をサポートせずにコンパイルしています。

4

1 に答える 1

0

はい、インラインは間違いなく必要です。
1) コンパイラは通常、インライン化された関数の新しいコピーを、それが呼び出された各場所に配置します。この最適化は、volatile 変数には影響しないようです。これでOKです。
2)私はこれを正しい解決策として(拡張子を付けて)2番目に挙げます。揮発性である必要がある唯一の変数は、実際にはキューインデックスであるためです。
3)いいえ、他の潜在的な最適化を妨げる可能性があるため、クラスインスタンス全体を揮発性とマークする必要はありません。
4) 継承を使用できます。キューに必要な関数を宣言するインターフェイスと、ISR を使用する (キュー インデックスが揮発性である) 場合と ISR を使用しない場合の 2 つの継承されたクラス。さらに、テンプレート化されたクラスをいつでも定義できます。

template<typename T>
class IQueue
{
public:
        virtual bool isEmpty() const = 0;
        virtual T pop() = 0;
protected:
    uint8_t pushIndex;
    T values[QUEUE_SIZE];
};


template<typename T>
class ISRQueue : public IQueue<T>
{
    volatile uint8_t popIndex;
public:
    inline bool isEmpty()const
    {
        return (pushIndex == popIndex);
    }

    inline T pop()
    {
        T value = values[popIndex++];
        popIndex &= MASK;
        return value;
    }
};

template<typename T>
class Queue : public IQueue<T>
{
    uint8_t popIndex;
public:
    inline bool isEmpty()const
    {
        return (pushIndex == popIndex);
    }

    inline T pop()
    {
        T value = values[popIndex++];
        popIndex &= MASK;
        return value;
    }
};

typedef ISRQueue<uint8_t> ISRQueueUInt;
typedef ISRQueue<uint8_t> QueueUInt;
于 2015-03-04T05:52:03.920 に答える