From 8027941b705e219fd202b7c01d5a4a311670cbee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Sat, 7 Apr 2018 21:13:42 +0200 Subject: Add initial implementatio of caches --- qtmips_machine/cache.cpp | 91 ++++++++++++++++++++++++++++++++++++++ qtmips_machine/cache.h | 27 +++++++++++ qtmips_machine/machineconfig.h | 4 +- qtmips_machine/tests/testcache.cpp | 61 +++++++++++++++++++++++++ qtmips_machine/tests/tests.pro | 3 +- qtmips_machine/tests/tst_machine.h | 3 ++ 6 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 qtmips_machine/tests/testcache.cpp (limited to 'qtmips_machine') 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 #include +#include 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("cache_c"); + QTest::addColumn("hit"); + QTest::addColumn("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 -- cgit v1.2.3