aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarel Kočí <cynerd@email.cz>2018-04-07 21:13:42 +0200
committerKarel Kočí <cynerd@email.cz>2018-04-07 21:13:42 +0200
commit8027941b705e219fd202b7c01d5a4a311670cbee (patch)
tree69ded0f03bea200586611c3916be82e4132f989a
parent1359b6a4bcc9f37ab8d550cd960bfa6bd9a86a36 (diff)
downloadqtmips-8027941b705e219fd202b7c01d5a4a311670cbee.tar.gz
qtmips-8027941b705e219fd202b7c01d5a4a311670cbee.tar.bz2
qtmips-8027941b705e219fd202b7c01d5a4a311670cbee.zip
Add initial implementatio of caches
-rw-r--r--qtmips_machine/cache.cpp91
-rw-r--r--qtmips_machine/cache.h27
-rw-r--r--qtmips_machine/machineconfig.h4
-rw-r--r--qtmips_machine/tests/testcache.cpp61
-rw-r--r--qtmips_machine/tests/tests.pro3
-rw-r--r--qtmips_machine/tests/tst_machine.h3
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