diff options
| -rw-r--r-- | qtmips_machine/cache.cpp | 91 | ||||
| -rw-r--r-- | qtmips_machine/cache.h | 27 | ||||
| -rw-r--r-- | qtmips_machine/machineconfig.h | 4 | ||||
| -rw-r--r-- | qtmips_machine/tests/testcache.cpp | 61 | ||||
| -rw-r--r-- | qtmips_machine/tests/tests.pro | 3 | ||||
| -rw-r--r-- | qtmips_machine/tests/tst_machine.h | 3 | 
6 files changed, 187 insertions, 2 deletions
| diff --git a/qtmips_machine/cache.cpp b/qtmips_machine/cache.cpp index 152b1c7..0f8bd1a 100644 --- a/qtmips_machine/cache.cpp +++ b/qtmips_machine/cache.cpp @@ -1,3 +1,94 @@  #include "cache.h"  using namespace machine; + +Cache::Cache(Memory *m, MachineConfigCache *cc) : cnf(cc) { +    mem = m; +    // Allocate cache data structure +    dt = new struct cache_data*[cc->associativity()]; +    for (unsigned i = 0; i < cc->associativity(); i++) { +        dt[i] = new cache_data[cc->sets()]; +        for (unsigned y = 0; y < cc->sets(); y++) { +            dt[i][y].valid = false; +            dt[i][y].data = new std::uint32_t[cc->blocks()]; +        } +    } +    // Zero hit and miss rate +    hitc = 0; +    missc = 0; +} + +void Cache::wword(std::uint32_t address, std::uint32_t value) { +    std::uint32_t *data; +    access(address, &data, false); +    *data = value; + +    if (cnf.write_policy() == MachineConfigCache::WP_TROUGH) +        mem->wword(address, value); +} + +std::uint32_t Cache::rword(std::uint32_t address) const { +    std::uint32_t *data; +    access(address, &data, true); +    return *data; +} + +void Cache::flush() { +    for (unsigned as = 0; as < cnf.associativity(); as++) { +        for (unsigned st = 0; st < cnf.sets(); st++) { +            struct cache_data &cd = dt[as][st]; +            if (cnf.write_policy() == MachineConfigCache::WP_BACK && cd.valid && cd.dirty) { +                std::uint32_t base_address = cd.tag * cnf.blocks() * cnf.sets(); +                for (unsigned i = 0; i < cnf.blocks(); i++) +                    mem->wword(base_address + (4*i), cd.data[i]); +            } +            cd.dirty = false; +            cd.valid = false; +        } +    } +} + +unsigned Cache::hit() { +    return hitc; +} + +unsigned Cache::miss() { +    return missc; +} + +void Cache::access(std::uint32_t address, std::uint32_t **data, bool read) const { +    // TODO associativity +    address = address >> 2; +    unsigned ssize = cnf.blocks() * cnf.sets(); +    std::uint32_t tag = address / ssize; +    std::uint32_t base_address = ((address / cnf.blocks()) * cnf.blocks()) << 2; +    std::uint32_t index = address % ssize; +    std::uint32_t row = index / cnf.blocks(); +    std::uint32_t col = index % cnf.blocks(); + +    struct cache_data &cd = dt[0][row]; + +    // Verify if we are not replacing +    if (cd.tag != tag && cd.valid) { +        if (cd.dirty && cnf.write_policy() == MachineConfigCache::WP_BACK) +            for (unsigned i = 0; i < cnf.blocks(); i++) +                mem->wword(base_address + (4*i), cd.data[i]); +        cd.valid = false; +        cd.dirty = false; +    } + +    if (cd.valid) { +        hitc++; +    } else { +        missc++; +        if (read) { // If this is read and we don't have valid content then read it from memory +            for (unsigned i = 0; i < cnf.blocks(); i++) +                cd.data[i] = mem->rword(base_address + (4*i)); +        } +    } + +    cd.valid = true; // We either write to it or we read from memory. Either way it's valid when we leave Cache class +    cd.dirty = cd.dirty || !read; +    cd.tag = tag; +    *data = &cd.data[col]; +} diff --git a/qtmips_machine/cache.h b/qtmips_machine/cache.h index 1bf82bf..3ad3bdb 100644 --- a/qtmips_machine/cache.h +++ b/qtmips_machine/cache.h @@ -3,12 +3,39 @@  #include <memory.h>  #include <machineconfig.h> +#include <stdint.h>  namespace machine {  class Cache : public MemoryAccess { +    Q_OBJECT  public:      Cache(Memory *m, MachineConfigCache *c); + +    void wword(std::uint32_t address, std::uint32_t value); +    std::uint32_t rword(std::uint32_t address) const; + +    void flush(); // flush/sync cache + +    unsigned hit(); // Number of recorded hits +    unsigned miss(); // Number of recorded misses + +    // TODO reset + +private: +    MachineConfigCache cnf; +    Memory *mem; + +    struct cache_data { +        bool valid, dirty; +        std::uint32_t tag; +        std::uint32_t *data; +    }; +    mutable struct cache_data **dt; + +    mutable unsigned hitc, missc; + +    void access(std::uint32_t address, std::uint32_t **data, bool read) const;  };  } diff --git a/qtmips_machine/machineconfig.h b/qtmips_machine/machineconfig.h index a713a4c..42406ff 100644 --- a/qtmips_machine/machineconfig.h +++ b/qtmips_machine/machineconfig.h @@ -37,7 +37,7 @@ public:      // If cache should be used or not      void set_enabled(bool); -    void set_sets(unsigned); // Number of sets bits used in cache +    void set_sets(unsigned); // Number of set      void set_blocks(unsigned); // Number of blocks      void set_associativity(unsigned); // Degree of associativity      void set_replacement_policy(enum ReplacementPolicy); @@ -125,4 +125,6 @@ private:  } +Q_DECLARE_METATYPE(machine::MachineConfigCache) +  #endif // MACHINECONFIG_H diff --git a/qtmips_machine/tests/testcache.cpp b/qtmips_machine/tests/testcache.cpp new file mode 100644 index 0000000..02e734e --- /dev/null +++ b/qtmips_machine/tests/testcache.cpp @@ -0,0 +1,61 @@ +#include "tst_machine.h" +#include "cache.h" + +using namespace machine; + +void MachineTests::cache_data() { +    QTest::addColumn<MachineConfigCache>("cache_c"); +    QTest::addColumn<unsigned>("hit"); +    QTest::addColumn<unsigned>("miss"); + +    MachineConfigCache cache_c; +    cache_c.set_write_policy(MachineConfigCache::WP_TROUGH); +    cache_c.set_enabled(true); +    cache_c.set_sets(8); +    cache_c.set_blocks(1); +    cache_c.set_associativity(1); +    QTest::newRow("Directly mapped") << cache_c \ +                                     << (unsigned)3 \ +                                     << (unsigned)7; +    cache_c.set_sets(1); +    cache_c.set_blocks(8); +    QTest::newRow("Wide") << cache_c \ +                                     << (unsigned)5 \ +                                     << (unsigned)5; +    cache_c.set_sets(4); +    cache_c.set_blocks(4); +    QTest::newRow("Square") << cache_c \ +                                     << (unsigned)4 \ +                                     << (unsigned)6; +} + +void MachineTests::cache() { +    QFETCH(MachineConfigCache, cache_c); +    QFETCH(unsigned, hit); +    QFETCH(unsigned, miss); + +    Memory m; +    Cache cch(&m, &cache_c); + +    // Test reads // +    m.write_word(0x200, 0x24); +    m.write_word(0x204, 0x66); +    m.write_word(0x21c, 0x12); +    m.write_word(0x300, 0x32); +    for (int i = 0; i < 2; i++) { +        QCOMPARE(cch.read_word(0x200), (std::uint32_t)0x24); +        QCOMPARE(cch.read_word(0x204), (std::uint32_t)0x66); +        QCOMPARE(cch.read_word(0x21c), (std::uint32_t)0x12); +        QCOMPARE(cch.read_word(0x300), (std::uint32_t)0x32); +    } + +    // Test writes // +    cch.write_word(0x700, 0x24); +    QCOMPARE(m.read_word(0x700), (std::uint32_t)0x24); +    cch.write_word(0x700, 0x23); +    QCOMPARE(m.read_word(0x700), (std::uint32_t)0x23); + +    // Verify counts +    QCOMPARE(cch.hit(), hit); +    QCOMPARE(cch.miss(), miss); +} diff --git a/qtmips_machine/tests/tests.pro b/qtmips_machine/tests/tests.pro index ffe75b7..751bf8a 100644 --- a/qtmips_machine/tests/tests.pro +++ b/qtmips_machine/tests/tests.pro @@ -21,7 +21,8 @@ SOURCES += tst_machine.cpp \      testprogramloader.cpp \      testinstruction.cpp \      testalu.cpp \ -    testcore.cpp +    testcore.cpp \ +    testcache.cpp  HEADERS += tst_machine.h diff --git a/qtmips_machine/tests/tst_machine.h b/qtmips_machine/tests/tst_machine.h index ad3112c..a0e8525 100644 --- a/qtmips_machine/tests/tst_machine.h +++ b/qtmips_machine/tests/tst_machine.h @@ -46,6 +46,9 @@ private Q_SLOTS:      void singlecore_mem_data();      void pipecore_mem();      void pipecore_mem_data(); +    // Cache +    void cache_data(); +    void cache();  };  #endif // TST_MACHINE_H | 
