diff options
-rw-r--r-- | qtmips_machine/alu.h | 3 | ||||
-rw-r--r-- | qtmips_machine/instruction.cpp | 8 | ||||
-rw-r--r-- | qtmips_machine/instruction.h | 8 | ||||
-rw-r--r-- | qtmips_machine/memory.cpp | 123 | ||||
-rw-r--r-- | qtmips_machine/memory.h | 33 | ||||
-rw-r--r-- | qtmips_machine/registers.cpp | 10 | ||||
-rw-r--r-- | qtmips_machine/registers.h | 4 | ||||
-rw-r--r-- | qtmips_machine/tests/testalu.cpp | 22 | ||||
-rw-r--r-- | qtmips_machine/tests/testcore.cpp | 23 | ||||
-rw-r--r-- | qtmips_machine/tests/testmemory.cpp | 28 | ||||
-rw-r--r-- | 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 <cstdint> #include <QString> +#include <QObject> // 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 <QObject> #include <qstring.h> -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<std::uint8_t>("op"); + QTest::addColumn<AluOp>("op"); QTest::addColumn<std::uint32_t>("s"); QTest::addColumn<std::uint32_t>("t"); QTest::addColumn<std::uint8_t>("sa"); QTest::addColumn<std::uint32_t>("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<Instruction>("i"); QTest::addColumn<Registers>("init"); QTest::addColumn<Registers>("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<Instruction>("i"); + QTest::addColumn<Registers>("init"); + QTest::addColumn<Registers>("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 |