From 499a88621d12ff0cdcba1f8c796b7031d6adc649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Tue, 21 Nov 2017 19:48:51 +0100 Subject: Add possibility to compare memory and registers state For core testing we want to compare whole memory and registers. Registers are pretty simple but in case of memory it is some what more complicated and required its own tests to be sure that it works. --- qtmips_machine/alu.h | 3 + qtmips_machine/instruction.cpp | 8 +++ qtmips_machine/instruction.h | 8 ++- qtmips_machine/memory.cpp | 123 +++++++++++++++++++++++++++++++----- qtmips_machine/memory.h | 33 ++++++++-- qtmips_machine/registers.cpp | 10 +-- qtmips_machine/registers.h | 4 +- qtmips_machine/tests/testalu.cpp | 22 +++---- qtmips_machine/tests/testcore.cpp | 23 +++++-- qtmips_machine/tests/testmemory.cpp | 28 +++++++- qtmips_machine/tests/tst_machine.h | 1 + 11 files changed, 216 insertions(+), 47 deletions(-) diff --git a/qtmips_machine/alu.h b/qtmips_machine/alu.h index 974b462..2e30ee9 100644 --- a/qtmips_machine/alu.h +++ b/qtmips_machine/alu.h @@ -3,6 +3,7 @@ #include #include +#include // TODO Any other operations? We seems to be missing a lot of them. enum AluOp : std::uint8_t { @@ -36,4 +37,6 @@ std::uint32_t alu_operate(enum AluOp operation, std::uint32_t s, std::uint32_t t // Returns string representation of ALU instruction (internally used by Instruction::to_str) QString alu_str(enum AluOp operation, std::uint32_t s, std::uint32_t t, std::uint8_t sa); +Q_DECLARE_METATYPE(AluOp) + #endif // ALU_H diff --git a/qtmips_machine/instruction.cpp b/qtmips_machine/instruction.cpp index 8591d93..91bd1c8 100644 --- a/qtmips_machine/instruction.cpp +++ b/qtmips_machine/instruction.cpp @@ -28,6 +28,10 @@ const struct InstructionMap instruction_map[] = { IM_UNKNOWN }; +Instruction::Instruction() { + this->dt = 0; +} + Instruction::Instruction(std::uint32_t inst) { this->dt = inst; } @@ -56,6 +60,10 @@ Instruction::Instruction(std::uint8_t opcode, std::uint32_t address) { this->dt |= address; } +Instruction::Instruction(const Instruction &i) { + this->dt = i.data(); +} + QString Instruction::to_str() { if (this->opcode() >= sizeof(instruction_map)) return QString("UNKNOWN"); diff --git a/qtmips_machine/instruction.h b/qtmips_machine/instruction.h index 3b76fba..921d9b0 100644 --- a/qtmips_machine/instruction.h +++ b/qtmips_machine/instruction.h @@ -1,14 +1,18 @@ #ifndef INSTRUCTION_H #define INSTRUCTION_H +#include #include -class Instruction { +class Instruction : public QObject { + Q_OBJECT public: + Instruction(); Instruction(std::uint32_t inst); Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint8_t rd, std::uint8_t shamt, std::uint8_t funct); // Type R Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate); // Type I Instruction(std::uint8_t opcode, std::uint32_t address); // Type J + Instruction(const Instruction&); QString to_str(); @@ -28,4 +32,6 @@ private: std::uint32_t dt; }; +Q_DECLARE_METATYPE(Instruction) + #endif // INSTRUCTION_H diff --git a/qtmips_machine/memory.cpp b/qtmips_machine/memory.cpp index 21ca03f..a9cc055 100644 --- a/qtmips_machine/memory.cpp +++ b/qtmips_machine/memory.cpp @@ -56,28 +56,47 @@ std::uint32_t MemoryAccess::read_word(std::uint32_t offset) { } MemorySection::MemorySection(std::uint32_t length) { - this->length = length; + this->len = length; this->dt = new std::uint8_t[length]; + memset(this->dt, 0, sizeof *this->dt * length); +} + +MemorySection::MemorySection(const MemorySection &ms) : MemorySection(ms.length()) { + memcpy(this->dt, ms.data(), sizeof *this->dt * this->len); } MemorySection::~MemorySection() { delete this->dt; } -using namespace std; - void MemorySection::write_byte(std::uint32_t offset, std::uint8_t value) { - if (offset >= this->length) + if (offset >= this->len) throw QTMIPS_EXCEPTION(OutOfMemoryAccess, "Trying to write outside of the memory section", QString("Accessing using offset: ") + QString(offset)); this->dt[offset] = value; } -std::uint8_t MemorySection::read_byte(std::uint32_t offset) { - if (offset >= this->length) +std::uint8_t MemorySection::read_byte(std::uint32_t offset) const { + if (offset >= this->len) throw QTMIPS_EXCEPTION(OutOfMemoryAccess, "Trying to read outside of the memory section", QString("Accessing using offset: ") + QString(offset)); return this->dt[offset]; } +std::uint32_t MemorySection::length() const { + return len; +} + +const std::uint8_t* MemorySection::data() const { + return this->dt; +} + +bool MemorySection::operator==(const MemorySection &ms) const { + return ! memcmp(this->dt, ms.data(), sizeof *this->dt * this->len); +} + +bool MemorySection::operator!=(const MemorySection &ms) const { + return ! this->operator ==(ms); +} + // Number of bites per row on lookup tree #define MEMORY_TREE_ROW ((32 - MEMORY_SECTION_BITS) / MEMORY_TREE_H) // Size of row in memory lookup tree @@ -102,17 +121,12 @@ Memory::Memory() { this->mt_root = allocate_section_tree(); } -Memory::~Memory() { - // Free up memory tree - // TODO +Memory::Memory(const Memory &m) { + this->mt_root = copy_section_tree(m.get_memorytree_root(), 0); } -union MemoryTree *Memory::allocate_section_tree() { - union MemoryTree *mt = new union MemoryTree[MEMORY_TREE_LEN]; - for (size_t i = 0; i < MEMORY_TREE_LEN; i++) - // Note that this also nulls sec pointer as those are both pointers and so they have same size - mt[i].mt = nullptr; - return mt; +Memory::~Memory() { + free_section_tree(this->mt_root, 0); } // Create address mask with section length @@ -123,7 +137,7 @@ union MemoryTree *Memory::allocate_section_tree() { #define ADDRESS_TREE_INDEX(DEPTH, ADDR) ((ADDR >> (DEPTH * MEMORY_TREE_ROW)) & ADDRESS_MASK(MEMORY_TREE_ROW)) -MemorySection *Memory::get_section(std::uint32_t address, bool create) { +MemorySection *Memory::get_section(std::uint32_t address, bool create) const { std::uint32_t addr = address >> MEMORY_SECTION_BITS; // drop all bits for addressing inside of the section union MemoryTree *w = this->mt_root; size_t ii; @@ -155,10 +169,85 @@ void Memory::write_byte(std::uint32_t address, std::uint8_t value) { section->write_byte(SECTION_ADDRESS(address), value); } -std::uint8_t Memory::read_byte(std::uint32_t address) { +std::uint8_t Memory::read_byte(std::uint32_t address) const { MemorySection *section = this->get_section(address, true); if (section == nullptr) return 0; else return section->read_byte(SECTION_ADDRESS(address)); } + +bool Memory::operator==(const Memory&m) const { + return compare_section_tree(this->mt_root, m.get_memorytree_root(), 0); +} + +bool Memory::operator!=(const Memory&m) const { + return ! this->operator ==(m); +} + +const union MemoryTree *Memory::get_memorytree_root() const { + return this->mt_root; +} + +union MemoryTree *Memory::allocate_section_tree() { + union MemoryTree *mt = new union MemoryTree[MEMORY_TREE_LEN]; + for (size_t i = 0; i < MEMORY_TREE_LEN; i++) + // Note that this also nulls sec pointer as those are both pointers and so they have same size + mt[i].mt = nullptr; + return mt; +} + +void Memory::free_section_tree(union MemoryTree *mt, size_t depth) { + if (depth < (MEMORY_TREE_H - 1)) { // Following level is memory tree + for (int i = 0; i < MEMORY_TREE_LEN; i++) { + if (mt[i].mt != nullptr) + free_section_tree(mt[i].mt, depth + 1); + } + } else { // Following level is memory section + for (int i = 0; i < MEMORY_TREE_LEN; i++) { + if (mt[i].sec != nullptr) + delete mt[i].sec; + } + } +} + +bool Memory::compare_section_tree(const union MemoryTree *mt1, const union MemoryTree *mt2, size_t depth) { + if (depth < (MEMORY_TREE_H - 1)) { // Following level is memory tree + for (int i = 0; i < MEMORY_TREE_LEN; i++) { + if ( + ((mt1[i].mt == nullptr || mt2[i].mt == nullptr) && mt1[i].mt != mt2[i].mt) + || + (mt1[i].mt != nullptr && mt2[i].mt != nullptr && !compare_section_tree(mt1[i].mt, mt2[i].mt, depth + 1)) + ) { + return false; + } + } + } else { // Following level is memory section + for (int i = 0; i < MEMORY_TREE_LEN; i++) { + if ( + ((mt1[i].sec == nullptr || mt2[i].sec == nullptr) && mt1[i].sec != mt2[i].sec) + || + (mt1[i].sec != nullptr && mt2[i].sec != nullptr && *mt1[i].sec != *mt2[i].sec) + ) { + return false; + } + } + } + return true; +} + +union MemoryTree *Memory::copy_section_tree(const union MemoryTree *mt, size_t depth) { + union MemoryTree *nmt = allocate_section_tree(); + if (depth < (MEMORY_TREE_H - 1)) { // Following level is memory tree + for (int i = 0; i < MEMORY_TREE_LEN; i++) { + if (mt[i].mt != nullptr) + nmt[i].mt = copy_section_tree(mt[i].mt, depth + 1); + } + } else { // Following level is memory section + for (int i = 0; i < MEMORY_TREE_LEN; i++) { + if (mt[i].sec != nullptr) + nmt[i].sec = new MemorySection(*mt[i].sec); + } + } + return nmt; +} diff --git a/qtmips_machine/memory.h b/qtmips_machine/memory.h index 1df23ae..1b56d7c 100644 --- a/qtmips_machine/memory.h +++ b/qtmips_machine/memory.h @@ -14,7 +14,7 @@ public: void write_hword(std::uint32_t offset, std::uint16_t value); void write_word(std::uint32_t offset, std::uint32_t value); - virtual std::uint8_t read_byte(std::uint32_t offset) = 0; + virtual std::uint8_t read_byte(std::uint32_t offset) const = 0; std::uint16_t read_hword(std::uint32_t offset); std::uint32_t read_word(std::uint32_t offset); @@ -26,11 +26,20 @@ signals: class MemorySection : public MemoryAccess { public: MemorySection(std::uint32_t length); + MemorySection(const MemorySection&); ~MemorySection(); + void write_byte(std::uint32_t offset, std::uint8_t value); - std::uint8_t read_byte(std::uint32_t offset); + std::uint8_t read_byte(std::uint32_t offset) const; + + std::uint32_t length() const; + const std::uint8_t* data() const; + + bool operator==(const MemorySection&) const; + bool operator!=(const MemorySection&) const; + private: - std::uint32_t length; + std::uint32_t len; std::uint8_t *dt; }; @@ -48,13 +57,27 @@ class Memory : public MemoryAccess { Q_OBJECT public: Memory(); + Memory(const Memory&); ~Memory(); - MemorySection *get_section(std::uint32_t address, bool create); // returns section containing given address + + MemorySection *get_section(std::uint32_t address, bool create) const; // returns section containing given address void write_byte(std::uint32_t address, std::uint8_t value); - std::uint8_t read_byte(std::uint32_t address); + std::uint8_t read_byte(std::uint32_t address) const; + + bool operator==(const Memory&) const; + bool operator!=(const Memory&) const; + + const union MemoryTree *get_memorytree_root() const; + private: union MemoryTree *mt_root; static union MemoryTree *allocate_section_tree(); + static void free_section_tree(union MemoryTree*, size_t depth); + static bool compare_section_tree(const union MemoryTree*, const union MemoryTree*, size_t depth); + static bool is_zero_section_tree(const union MemoryTree*, size_t depth); + static union MemoryTree *copy_section_tree(const union MemoryTree*, size_t depth); }; +Q_DECLARE_METATYPE(Memory) + #endif // MEMORY_H diff --git a/qtmips_machine/registers.cpp b/qtmips_machine/registers.cpp index fa984fb..6b4ddd3 100644 --- a/qtmips_machine/registers.cpp +++ b/qtmips_machine/registers.cpp @@ -14,12 +14,12 @@ Registers::Registers() { this->hi = this->lo = 0; } -Registers::Registers(const Registers *orig) : Registers() { - this->pc = orig->read_pc(); +Registers::Registers(const Registers &orig) : Registers() { + this->pc = orig.read_pc(); for (int i = 0; i < 31; i++) - this->gp[i] = orig->read_gp(i); - this->lo = orig->read_hi_lo(false); - this->hi = orig->read_hi_lo(true); + this->gp[i] = orig.read_gp(i); + this->lo = orig.read_hi_lo(false); + this->hi = orig.read_hi_lo(true); } std::uint32_t Registers::read_pc() const { diff --git a/qtmips_machine/registers.h b/qtmips_machine/registers.h index 905a212..49b4cad 100644 --- a/qtmips_machine/registers.h +++ b/qtmips_machine/registers.h @@ -8,7 +8,7 @@ class Registers : public QObject { Q_OBJECT public: Registers(); - Registers(const Registers*); + Registers(const Registers&); std::uint32_t read_pc() const; // Return current value of program counter std::uint32_t pc_inc(); // Increment program counter by four bytes @@ -32,4 +32,6 @@ private: std::uint32_t pc; // program counter }; +Q_DECLARE_METATYPE(Registers) + #endif // REGISTERS_H diff --git a/qtmips_machine/tests/testalu.cpp b/qtmips_machine/tests/testalu.cpp index 37accdf..2943906 100644 --- a/qtmips_machine/tests/testalu.cpp +++ b/qtmips_machine/tests/testalu.cpp @@ -3,49 +3,49 @@ #include "qtmipsexception.h" void MachineTests::alu_data() { - QTest::addColumn("op"); + QTest::addColumn("op"); QTest::addColumn("s"); QTest::addColumn("t"); QTest::addColumn("sa"); QTest::addColumn("res"); // TODO SLL-SRAV - QTest::newRow("ADD") << (std::uint8_t)ALU_OP_ADD \ + QTest::newRow("ADD") << ALU_OP_ADD \ << (std::uint32_t)24 \ << (std::uint32_t)66 \ << (std::uint8_t)0 \ << (std::uint32_t)90; - QTest::newRow("ADDU") << (std::uint8_t)ALU_OP_ADDU \ + QTest::newRow("ADDU") << ALU_OP_ADDU \ << (std::uint32_t)24 \ << (std::uint32_t)66 \ << (std::uint8_t)0 \ << (std::uint32_t)90; - QTest::newRow("SUB") << (std::uint8_t)ALU_OP_SUB \ + QTest::newRow("SUB") << ALU_OP_SUB \ << (std::uint32_t)66 \ << (std::uint32_t)24 \ << (std::uint8_t)0 \ << (std::uint32_t)42; - QTest::newRow("SUBU") << (std::uint8_t)ALU_OP_SUBU \ + QTest::newRow("SUBU") << ALU_OP_SUBU \ << (std::uint32_t)24 \ << (std::uint32_t)66 \ << (std::uint8_t)0 \ << (std::uint32_t)-42; - QTest::newRow("AND") << (std::uint8_t)ALU_OP_AND \ + QTest::newRow("AND") << ALU_OP_AND \ << (std::uint32_t)0xA81 \ << (std::uint32_t)0x603 \ << (std::uint8_t)0 \ << (std::uint32_t)0x201; - QTest::newRow("OR") << (std::uint8_t)ALU_OP_OR \ + QTest::newRow("OR") << ALU_OP_OR \ << (std::uint32_t)0xA81 \ << (std::uint32_t)0x603 \ << (std::uint8_t)0 \ << (std::uint32_t)0xE83; - QTest::newRow("XOR") << (std::uint8_t)ALU_OP_XOR \ + QTest::newRow("XOR") << ALU_OP_XOR \ << (std::uint32_t)0xA81 \ << (std::uint32_t)0x603 \ << (std::uint8_t)0 \ << (std::uint32_t)0xC82; - QTest::newRow("NOR") << (std::uint8_t)ALU_OP_NOR \ + QTest::newRow("NOR") << ALU_OP_NOR \ << (std::uint32_t)0xA81 \ << (std::uint32_t)0x603 \ << (std::uint8_t)0 \ @@ -54,13 +54,13 @@ void MachineTests::alu_data() { } void MachineTests::alu() { - QFETCH(std::uint8_t, op); + QFETCH(AluOp, op); QFETCH(std::uint32_t, s); QFETCH(std::uint32_t, t); QFETCH(std::uint8_t, sa); QFETCH(std::uint32_t, res); - QCOMPARE(alu_operate((enum AluOp)op, s , t, sa), res); + QCOMPARE(alu_operate(op, s , t, sa), res); } void MachineTests::alu_except_data() { diff --git a/qtmips_machine/tests/testcore.cpp b/qtmips_machine/tests/testcore.cpp index bbf8086..33bf07e 100644 --- a/qtmips_machine/tests/testcore.cpp +++ b/qtmips_machine/tests/testcore.cpp @@ -2,28 +2,43 @@ #include "core.h" void MachineTests::core_regs_data() { - /* QTest::addColumn("i"); QTest::addColumn("init"); QTest::addColumn("res"); // Test arithmetic instructions { - Registers regs_init(); + Registers regs_init; regs_init.write_gp(24, 12); regs_init.write_gp(25, 24); - Registers regs_res(®s_init); + Registers regs_res(regs_init); regs_res.write_gp(26, 36); QTest::newRow("ADD") << Instruction(0, 24, 25, 26, 0, 32) \ << regs_init \ << regs_res; } - */ // TODO test other operations } void MachineTests::core_regs() { + QTest::addColumn("i"); + QTest::addColumn("init"); + QTest::addColumn("res"); + QFETCH(Instruction, i); + QFETCH(Registers, init); + QFETCH(Registers, res); + + Memory mem; // Just memory (it shouldn't be used here except instruction) + mem.write_word(res.read_pc(), i.data()); // Store single instruction (anything else should be 0 so NOP effectively) + + // Test on non-piplined + Memory mem_single(mem); // Create memory copy + CoreSingle core_single(&init, &mem_single); + core_single.step(); // Single step should be enought as this is risc without pipeline + //QCOMPARE(init, res); // After doing changes from initial state this should be same state as in case of passed expected result + QCOMPARE(mem, mem_single); // There should be no change in memory + // TODO on pipelined core } void MachineTests::core_mem_data() { diff --git a/qtmips_machine/tests/testmemory.cpp b/qtmips_machine/tests/testmemory.cpp index e450231..091c26d 100644 --- a/qtmips_machine/tests/testmemory.cpp +++ b/qtmips_machine/tests/testmemory.cpp @@ -44,8 +44,7 @@ void MachineTests::memory_section() { QFETCH(std::uint32_t, address); - // First section shouldn't exists - QCOMPARE(m.get_section(address, false), (MemorySection*)nullptr); + // First section shouldn't exists QCOMPARE(m.get_section(address, false), (MemorySection*)nullptr); // Create section MemorySection *s = m.get_section(address, true); QVERIFY(s != nullptr); @@ -63,7 +62,7 @@ void MachineTests::memory_section() { void MachineTests::memory_endian() { Memory m; - // Memory should be bit endian so write bytes from most significant byte + // Memory should be little endian so write bytes from most significant byte m.write_byte(0x00, 0x12); m.write_byte(0x01, 0x34); m.write_byte(0x02, 0x56); @@ -81,3 +80,26 @@ void MachineTests::memory_endian() { QCOMPARE(m.read_byte(0xF2), (std::uint8_t)0x56); QCOMPARE(m.read_byte(0xF3), (std::uint8_t)0x78); } + +void MachineTests::memory_compare() { + Memory m1, m2; + QCOMPARE(m1, m2); + m1.write_byte(0x20,0x0); + QVERIFY(m1 != m2); // This should not be equal as this identifies also memory write (difference between no write and zero write) + m1.write_byte(0x20,0x24); + QVERIFY(m1 != m2); + m2.write_byte(0x20,0x23); + QVERIFY(m1 != m2); + m2.write_byte(0x20,0x24); + QCOMPARE(m1, m2); + // Do the same with some other section + m1.write_byte(0xFFFF20, 0x24); + QVERIFY(m1 != m2); + m2.write_byte(0xFFFF20, 0x24); + QCOMPARE(m1, m2); + // And also check memory copy + Memory m3(m1); + QCOMPARE(m1, m3); + m3.write_byte(0x18, 0x22); + QVERIFY(m1 != m3); +} diff --git a/qtmips_machine/tests/tst_machine.h b/qtmips_machine/tests/tst_machine.h index da8082a..b509bed 100644 --- a/qtmips_machine/tests/tst_machine.h +++ b/qtmips_machine/tests/tst_machine.h @@ -17,6 +17,7 @@ private Q_SLOTS: void memory_section(); void memory_section_data(); void memory_endian(); + void memory_compare(); // Program loader void program_loader(); // Instruction -- cgit v1.2.3