CodeBlanks Logo Icon CodeBlanks

Embedded

Gömülü kısıtlarla optimize edilmiş kod yazarsın.

volatile ve bellek eşlemeli I/O

Bellek eşlemeli I/O (MMIO), donanım kayıtlarının normal bellek adreslerinde görünmesidir. Bir kaydı okumak için o adresteki değeri okuruz. volatile anahtar kelimesi derleyiciye "her okumada bellekten oku, önbelleğe alma" der.

Örnek: volatile std::uint32_t* reg bir donanım kaydına işaret eder. *reg = 64; yazma, *reg okuma yapar. Derleyici bu erişimleri atlayamaz. Bu derste volatile std::uint32_t değişkenini bir sahte kayıt olarak kullanıp MMIO desenini öğreneceğiz.

Derleyici optimizasyonu ve volatile

Derleyici en iyileyici (optimizer), bir değişkeni birden çok kez okuduğunda önceki sonucu yeniden kullanabilir. Normal bir değişkende bu güvenlidir; çünkü değer program tarafından değişmedikçe aynı kalır. Donanım kaydında ise değer program dışında değişebilir; önbelleğe alınmış okuma yanlış olur.

volatile bu optimizasyonu kapatır. Her *reg okuması belleğe gider. Bu dersin örneğinde sahte kaydı 64 değeriyle yazar, sonra geri okuruz. Sonuç 64 olur; volatile sayesinde okuma atlanmaz.

Uygulama readRegister ve writeRegister metodlarını ekle

Önceki dersin çözümünü al (Engine sınıfında parallelSum var). Engine sınıfının public: bölümüne iki statik metot ekle: static std::uint32_t readRegister(volatile std::uint32_t* reg) gövdesi return *reg;, static void writeRegister(volatile std::uint32_t* reg, std::uint32_t value) gövdesi *reg = value;. main içinde parallelSum çağrısından sonra şunları ekle: volatile std::uint32_t reg = 0;, Engine::writeRegister(&reg, 64);, std::cout << std::endl << Engine::readRegister(&reg);. #include <cstdint> gereklidir.

Başlangıç kodu

main.cpp
#include <iostream>
#include <atomic>
#include <thread>
#include <cstdint>

class Engine {
public:
    static const int Version = 1;
    static int parallelSum() {
        std::atomic<int> total{0};
        std::thread t1([&]() { for (int i = 1; i <= 4; ++i) total += i; });
        std::thread t2([&]() { for (int i = 5; i <= 8; ++i) total += i; });
        t1.join();
        t2.join();
        return total.load();
    }
    // readRegister ve writeRegister buraya ekle
};

template <int N>
struct Factorial {
    static const int value = N * Factorial<N - 1>::value;
};

template <>
struct Factorial<0> {
    static const int value = 1;
};

int main() {
    std::cout << "Hazır" << std::endl;
    std::cout << Factorial<5>::value << std::endl;
    std::cout << Engine::parallelSum() << std::endl;
    // Kayıt erişimini buraya ekle
    return 0;
}

Örnek çözüm

main.cpp
#include <iostream>
#include <atomic>
#include <thread>
#include <cstdint>

class Engine {
public:
    static const int Version = 1;
    static int parallelSum() {
        std::atomic<int> total{0};
        std::thread t1([&]() { for (int i = 1; i <= 4; ++i) total += i; });
        std::thread t2([&]() { for (int i = 5; i <= 8; ++i) total += i; });
        t1.join();
        t2.join();
        return total.load();
    }
    static std::uint32_t readRegister(volatile std::uint32_t* reg) {
        return *reg;
    }
    static void writeRegister(volatile std::uint32_t* reg, std::uint32_t value) {
        *reg = value;
    }
};

template <int N>
struct Factorial {
    static const int value = N * Factorial<N - 1>::value;
};

template <>
struct Factorial<0> {
    static const int value = 1;
};

int main() {
    std::cout << "Hazır" << std::endl;
    std::cout << Factorial<5>::value << std::endl;
    std::cout << Engine::parallelSum() << std::endl;
    volatile std::uint32_t reg = 0;
    Engine::writeRegister(&reg, 64);
    std::cout << Engine::readRegister(&reg);
    return 0;
}

Beklenen çıktı

Hazır
120
36
64

Soru ve cevaplar

Dersin sorularını cevaplarıyla birlikte inceleyebilirsin. Kendini denemek istersen dersi interaktif olarak çöz.

1 Kayıt işaretçisini tamamla

Soru
std::uint32_t readRegister(volatile std::uint32_t* ____) { return *reg; }
Cevap
std::uint32_t readRegister(volatile std::uint32_t* reg) { return *reg; }

Açıklama: İşaretçi adı reg olarak kullanılır.

2 Parçayı rolüne eşle

Kavram Anlamı
volatile Optimizasyonu kapatır
std::uint32_t* Kayıt işaretçisi
*reg = 64 Kayda yazma
*reg Kayıttan okuma

3 volatile anahtar kelimesi ne işe yarar?

main.cpp
volatile std::uint32_t* reg;
  • Değişkeni sabit yapar
  • Derleyiciye okumayı atlamamasını söyler Doğru cevap
  • Değişkeni otomatik sıfırlar
  • Değişkeni iş parçacığı yerel yapar

Açıklama: volatile her okumayı belleğe zorlar; önbelleğe almaz.

Uzman bölümündeki tüm dersler