Skip to content
Permalink
Browse files

cpu: added icache

  • Loading branch information
JaCzekanski committed Nov 21, 2019
1 parent bb19c3b commit 00fdbc1e30324553f1d0c80cc0e6ec7d854a46cd
Showing with 126 additions and 10 deletions.
  1. +26 βˆ’2 src/cpu/cpu.cpp
  2. +17 βˆ’0 src/cpu/cpu.h
  3. +13 βˆ’0 src/device/cache_control.cpp
  4. +54 βˆ’0 src/device/cache_control.h
  5. +1 βˆ’1 src/device/serial.cpp
  6. +1 βˆ’1 src/state/state.cpp
  7. +11 βˆ’6 src/system.cpp
  8. +3 βˆ’0 src/system.h
@@ -7,11 +7,13 @@ namespace mips {
CPU::CPU(System* sys) : sys(sys), _opcode(0) {
setPC(0xBFC00000);
inBranchDelay = false;
icacheEnabled = false;
for (auto& r : reg) r = 0;
hi = 0;
lo = 0;

for (auto& slot : slots) slot.reg = 0;
for (auto& line : icache) line.tag = 0;
}

void CPU::loadDelaySlot(uint32_t r, uint32_t data) {
@@ -79,10 +81,32 @@ INLINE bool CPU::handleSoftwareBreakpoints() {
return false;
}

INLINE uint32_t CPU::fetchInstruction(uint32_t address) {
// Only KUSEG and KSEG0 have code-cache
// I'm completely clueless if address comparison here makes any sense
// Host CPU branch predictor sure isn't happy about it
if (unlikely(!icacheEnabled) || address >= 0xa000'0000) {
return sys->readMemory32(address);
}

uint32_t tag = ((address & 0xfffff000) >> 12) | CACHE_LINE_VALID_BIT;
uint16_t index = (address & 0xffc) >> 2;

CacheLine line = icache[index];
if (line.tag == tag) {
return line.data;
}

uint32_t data = sys->readMemory32(address);
icache[index] = CacheLine{tag, data};

return data;
}

bool CPU::executeInstructions(int count) {
for (int i = 0; i < count; i++) {
// HACK: BIOS hooks
uint32_t maskedPc = PC & 0x1FFFFF;
uint32_t maskedPc = PC & 0x1fff'ffff;
if (maskedPc == 0xa0 || maskedPc == 0xb0 || maskedPc == 0xc0) sys->handleBiosFunction();

saveStateForException();
@@ -91,7 +115,7 @@ bool CPU::executeInstructions(int count) {

if (unlikely(handleSoftwareBreakpoints())) return false;

_opcode = Opcode(sys->readMemory32(PC));
_opcode = Opcode(fetchInstruction(PC));
const auto& op = instructions::OpcodeTable[_opcode.op];

setPC(nextPC);
@@ -51,6 +51,18 @@ struct LoadSlot {
}
};

#define CACHE_LINE_VALID_BIT 0x80000000
struct CacheLine {
// Valid bit is stored as highest bit of of tag field
uint32_t tag; // 20 bit used
uint32_t data;

template <class Archive>
void serialize(Archive& ar) {
ar(tag, data);
}
};

struct CPU {
inline static const int REGISTER_COUNT = 32;

@@ -72,6 +84,9 @@ struct CPU {
System* sys;
Opcode _opcode;

bool icacheEnabled;
CacheLine icache[1024];

CPU(System* sys);
void checkForInterrupts();
void loadDelaySlot(uint32_t r, uint32_t data);
@@ -97,6 +112,7 @@ struct CPU {
void saveStateForException();
void handleHardwareBreakpoints();
INLINE bool handleSoftwareBreakpoints();
INLINE uint32_t fetchInstruction(uint32_t address);
bool executeInstructions(int count);

void busError();
@@ -119,6 +135,7 @@ struct CPU {
ar(reg, hi, lo);
ar(cop0);
ar(gte);
ar(icacheEnabled, icache);
}
};
}; // namespace mips
@@ -0,0 +1,13 @@
#include "cache_control.h"
#include "system.h"

CacheControl::CacheControl(System* sys) : sys(sys) { reset(); }

void CacheControl::reset() { cache._reg = 0; }

uint32_t CacheControl::read(uint32_t address) { return cache._reg; }

void CacheControl::write(uint32_t address, uint32_t data) {
cache._reg = data;
sys->cpu->icacheEnabled = cache.icacheEnable;
}
@@ -0,0 +1,54 @@
#pragma once
#include "device.h"

struct System;

union CacheConfiguration {
struct {
uint32_t lock : 1; // lock, Tag Test Mode
uint32_t inv : 1; // inv, Invalidate Mode
uint32_t tag : 1; // tag, Tag Test Mode (used by BIOS when invalidating icache)
uint32_t scratchpad : 1; // ram, use dcache as scratchpad (ignore valid bits)

uint32_t dcacheRefillSize : 2; // dblksz
uint32_t : 1;
uint32_t dcacheEnable : 1; // ds, used with bit3 to enable scratchpad

uint32_t icacheRefillSize : 2; // iblksz
uint32_t is0 : 1; // is0, enable icache set 0 - cleared to 0
uint32_t icacheEnable : 1; // is1, enable icache set 1 - 1 to enable

uint32_t interruptPolarity : 1; // intp, not used, should be 0
uint32_t enableReadPriority : 1; // rdpri, loads operations will have priority over store
uint32_t noWaitState : 1; // nopad
uint32_t enableBusGrant : 1; // bgnt, should be 0

uint32_t loadScheduling : 1; // ldsch
uint32_t noStreaming : 1; // nostr, not used, should be 0

uint32_t : 14;
};
uint32_t _reg;

template <class Archive>
void serialize(Archive& ar) {
ar(_reg);
}
};

class CacheControl {
System* sys;

CacheConfiguration cache;

public:
CacheControl(System* sys);
void reset();
uint32_t read(uint32_t address);
void write(uint32_t address, uint32_t data);

template <class Archive>
void serialize(Archive& ar) {
ar(cache);
}
};
@@ -20,7 +20,7 @@ uint8_t Serial::read(uint32_t address) {
}

void Serial::write(uint32_t address, uint8_t data) {
fmt::print("[SERIAL] write @ 0x{:02x}: 0x{:02x}\n", address, data);
// fmt::print("[SERIAL] write @ 0x{:02x}: 0x{:02x}\n", address, data);
if (address >= 14 && address < 16) {
baud._byte[address - 14] = data;
}
@@ -21,7 +21,7 @@ const std::string basePath = "data/state/";
const std::string lastSavePath = basePath + "last.state";

struct StateMetadata {
inline static const uint32_t SAVESTATE_VERSION = 1;
inline static const uint32_t SAVESTATE_VERSION = 2;

uint32_t version = SAVESTATE_VERSION;
std::string biosPath;
@@ -26,6 +26,7 @@ System::System() {
expansion2 = std::make_unique<Expansion2>();
interrupt = std::make_unique<Interrupt>(this);
memoryControl = std::make_unique<MemoryControl>();
cacheControl = std::make_unique<CacheControl>(this);
serial = std::make_unique<Serial>();
for (int t : {0, 1, 2}) {
timer[t] = std::make_unique<device::timer::Timer>(this, t);
@@ -170,8 +171,8 @@ INLINE T System::readMemory(uint32_t address) {
READ_IO(0x1f801C00, 0x1f802000, spu);
READ_IO(0x1f802000, 0x1f802067, expansion2);

if (in_range<0xfffe0130, 4>(address)) {
auto data = read_io<T>(memoryControl, address);
if (in_range<0xfffe0130, 4>(address) && sizeof(T) == 4) {
auto data = cacheControl->read(0);
LOG_IO(IO_LOG_ENTRY::MODE::READ, sizeof(T) * 8, address, data, cpu->PC);
return data;
}
@@ -185,7 +186,12 @@ template <typename T>
INLINE void System::writeMemory(uint32_t address, T data) {
static_assert(std::is_same<T, uint8_t>() || std::is_same<T, uint16_t>() || std::is_same<T, uint32_t>(), "Invalid type used");

if (unlikely(cpu->cop0.status.isolateCache)) return;
if (unlikely(cpu->cop0.status.isolateCache)) {
uint32_t tag = (address & 0xfffff000) >> 12;
uint16_t index = (address & 0xffc) >> 2;
cpu->icache[index] = mips::CacheLine{tag, data};
return;
}

uint32_t addr = align_mips<T>(address);

@@ -213,10 +219,9 @@ INLINE void System::writeMemory(uint32_t address, T data) {
WRITE_IO32(0x1f801820, 0x1f801828, mdec);
WRITE_IO(0x1f801C00, 0x1f802000, spu);
WRITE_IO(0x1f802000, 0x1f802067, expansion2);
WRITE_IO32(0xfffe0130, 0xfffe0134, memoryControl);

if (in_range<0xfffe0130, 4>(address)) {
write_io<T>(memoryControl, 0xfffe0130, data);
if (in_range<0xfffe0130, 4>(address) && sizeof(T) == 4) {
cacheControl->write(0, data);
LOG_IO(IO_LOG_ENTRY::MODE::WRITE, sizeof(T) * 8, address, data, cpu->PC);
return;
}
@@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include "cpu/cpu.h"
#include "device/cache_control.h"
#include "device/cdrom/cdrom.h"
#include "device/controller/controller.h"
#include "device/dma/dma.h"
@@ -74,6 +75,7 @@ struct System {
std::unique_ptr<Interrupt> interrupt;
std::unique_ptr<mdec::MDEC> mdec;
std::unique_ptr<MemoryControl> memoryControl;
std::unique_ptr<CacheControl> cacheControl;
std::unique_ptr<spu::SPU> spu;
std::unique_ptr<Serial> serial;
std::array<std::unique_ptr<device::timer::Timer>, 3> timer;
@@ -129,6 +131,7 @@ struct System {
ar(*dma);
ar(*cdrom);
ar(*memoryControl);
ar(*cacheControl);
ar(*serial);
ar(*mdec);
ar(*controller);

0 comments on commit 00fdbc1

Please sign in to comment.
You can’t perform that action at this time.