/*
|
/*
|
* mmu.c -- MMU simulation
|
* mmu.c -- MMU simulation
|
*/
|
*/
|
|
|
|
|
#include <stdio.h>
|
#include <stdio.h>
|
#include <stdlib.h>
|
#include <stdlib.h>
|
#include <string.h>
|
#include <string.h>
|
#include <setjmp.h>
|
#include <setjmp.h>
|
|
|
#include "common.h"
|
#include "common.h"
|
#include "console.h"
|
#include "console.h"
|
#include "error.h"
|
#include "error.h"
|
#include "except.h"
|
#include "except.h"
|
#include "cpu.h"
|
#include "cpu.h"
|
#include "mmu.h"
|
#include "mmu.h"
|
#include "memory.h"
|
#include "memory.h"
|
|
|
|
|
static Bool debugUse = false;
|
static Bool debugUse = false;
|
static Bool debugWrite = false;
|
static Bool debugWrite = false;
|
|
|
|
|
static TLB_Entry tlb[TLB_SIZE];
|
static TLB_Entry tlb[TLB_SIZE];
|
static Word tlbIndex;
|
static Word tlbIndex;
|
static Word tlbEntryHi;
|
static Word tlbEntryHi;
|
static Word tlbEntryLo;
|
static Word tlbEntryLo;
|
static Word mmuBadAddr;
|
static Word mmuBadAddr;
|
|
static Word mmuBadAccs;
|
|
|
|
|
static int assoc(Word page) {
|
static int assoc(Word page) {
|
int n, i;
|
int n, i;
|
|
|
n = -1;
|
n = -1;
|
for (i = 0; i < TLB_SIZE; i++) {
|
for (i = 0; i < TLB_SIZE; i++) {
|
if (tlb[i].page == page) {
|
if (tlb[i].page == page) {
|
n = i;
|
n = i;
|
}
|
}
|
}
|
}
|
return n;
|
return n;
|
}
|
}
|
|
|
|
|
static Word v2p(Word vAddr, Bool userMode, Bool writing) {
|
static Word v2p(Word vAddr, Bool userMode, Bool writing, int accsWidth) {
|
Word pAddr;
|
Word pAddr;
|
Word page, offset;
|
Word page, offset;
|
int index;
|
int index;
|
|
|
if (debugUse) {
|
if (debugUse) {
|
cPrintf("**** vAddr = 0x%08X", vAddr);
|
cPrintf("**** vAddr = 0x%08X", vAddr);
|
}
|
}
|
if ((vAddr & 0x80000000) != 0 && userMode) {
|
if ((vAddr & 0x80000000) != 0 && userMode) {
|
/* trying to access a privileged address from user mode */
|
/* trying to access a privileged address from user mode */
|
|
mmuBadAccs = (writing ? MMU_ACCS_WRITE : MMU_ACCS_READ) | accsWidth;
|
mmuBadAddr = vAddr;
|
mmuBadAddr = vAddr;
|
throwException(EXC_PRV_ADDRESS);
|
throwException(EXC_PRV_ADDRESS);
|
}
|
}
|
if ((vAddr & 0xC0000000) == 0xC0000000) {
|
if ((vAddr & 0xC0000000) == 0xC0000000) {
|
/* unmapped address space */
|
/* unmapped address space */
|
/* simulate delay introduced by assoc when using mapped
|
/* simulate delay introduced by assoc when using mapped
|
addresses but not experienced with unmapped addresses */
|
addresses but not experienced with unmapped addresses */
|
assoc(0);
|
assoc(0);
|
pAddr = vAddr & ~0xC0000000;
|
pAddr = vAddr & ~0xC0000000;
|
} else {
|
} else {
|
/* mapped address space */
|
/* mapped address space */
|
page = vAddr & PAGE_MASK;
|
page = vAddr & PAGE_MASK;
|
offset = vAddr & OFFSET_MASK;
|
offset = vAddr & OFFSET_MASK;
|
index = assoc(page);
|
index = assoc(page);
|
if (index == -1) {
|
if (index == -1) {
|
/* TLB miss exception */
|
/* TLB miss exception */
|
|
mmuBadAccs = (writing ? MMU_ACCS_WRITE : MMU_ACCS_READ) | accsWidth;
|
mmuBadAddr = vAddr;
|
mmuBadAddr = vAddr;
|
tlbEntryHi = page;
|
tlbEntryHi = page;
|
throwException(EXC_TLB_MISS);
|
throwException(EXC_TLB_MISS);
|
}
|
}
|
if (!tlb[index].valid) {
|
if (!tlb[index].valid) {
|
/* TLB invalid exception */
|
/* TLB invalid exception */
|
|
mmuBadAccs = (writing ? MMU_ACCS_WRITE : MMU_ACCS_READ) | accsWidth;
|
mmuBadAddr = vAddr;
|
mmuBadAddr = vAddr;
|
tlbEntryHi = page;
|
tlbEntryHi = page;
|
throwException(EXC_TLB_INVALID);
|
throwException(EXC_TLB_INVALID);
|
}
|
}
|
if (!tlb[index].write && writing) {
|
if (!tlb[index].write && writing) {
|
/* TLB write exception */
|
/* TLB write exception */
|
|
mmuBadAccs = (writing ? MMU_ACCS_WRITE : MMU_ACCS_READ) | accsWidth;
|
mmuBadAddr = vAddr;
|
mmuBadAddr = vAddr;
|
tlbEntryHi = page;
|
tlbEntryHi = page;
|
throwException(EXC_TLB_WRITE);
|
throwException(EXC_TLB_WRITE);
|
}
|
}
|
pAddr = tlb[index].frame | offset;
|
pAddr = tlb[index].frame | offset;
|
}
|
}
|
if (debugUse) {
|
if (debugUse) {
|
cPrintf(", pAddr = 0x%08X ****\n", pAddr);
|
cPrintf(", pAddr = 0x%08X ****\n", pAddr);
|
}
|
}
|
return pAddr;
|
return pAddr;
|
}
|
}
|
|
|
|
|
Word mmuReadWord(Word vAddr, Bool userMode) {
|
Word mmuReadWord(Word vAddr, Bool userMode) {
|
if ((vAddr & 3) != 0) {
|
if ((vAddr & 3) != 0) {
|
/* throw illegal address exception */
|
/* throw illegal address exception */
|
|
mmuBadAccs = MMU_ACCS_READ | MMU_ACCS_WORD;
|
mmuBadAddr = vAddr;
|
mmuBadAddr = vAddr;
|
throwException(EXC_ILL_ADDRESS);
|
throwException(EXC_ILL_ADDRESS);
|
}
|
}
|
return memoryReadWord(v2p(vAddr, userMode, false));
|
return memoryReadWord(v2p(vAddr, userMode, false, MMU_ACCS_WORD));
|
}
|
}
|
|
|
|
|
Half mmuReadHalf(Word vAddr, Bool userMode) {
|
Half mmuReadHalf(Word vAddr, Bool userMode) {
|
if ((vAddr & 1) != 0) {
|
if ((vAddr & 1) != 0) {
|
/* throw illegal address exception */
|
/* throw illegal address exception */
|
|
mmuBadAccs = MMU_ACCS_READ | MMU_ACCS_HALF;
|
mmuBadAddr = vAddr;
|
mmuBadAddr = vAddr;
|
throwException(EXC_ILL_ADDRESS);
|
throwException(EXC_ILL_ADDRESS);
|
}
|
}
|
return memoryReadHalf(v2p(vAddr, userMode, false));
|
return memoryReadHalf(v2p(vAddr, userMode, false, MMU_ACCS_HALF));
|
}
|
}
|
|
|
|
|
Byte mmuReadByte(Word vAddr, Bool userMode) {
|
Byte mmuReadByte(Word vAddr, Bool userMode) {
|
return memoryReadByte(v2p(vAddr, userMode, false));
|
return memoryReadByte(v2p(vAddr, userMode, false, MMU_ACCS_BYTE));
|
}
|
}
|
|
|
|
|
void mmuWriteWord(Word vAddr, Word data, Bool userMode) {
|
void mmuWriteWord(Word vAddr, Word data, Bool userMode) {
|
if ((vAddr & 3) != 0) {
|
if ((vAddr & 3) != 0) {
|
/* throw illegal address exception */
|
/* throw illegal address exception */
|
|
mmuBadAccs = MMU_ACCS_WRITE | MMU_ACCS_WORD;
|
mmuBadAddr = vAddr;
|
mmuBadAddr = vAddr;
|
throwException(EXC_ILL_ADDRESS);
|
throwException(EXC_ILL_ADDRESS);
|
}
|
}
|
memoryWriteWord(v2p(vAddr, userMode, true), data);
|
memoryWriteWord(v2p(vAddr, userMode, true, MMU_ACCS_WORD), data);
|
}
|
}
|
|
|
|
|
void mmuWriteHalf(Word vAddr, Half data, Bool userMode) {
|
void mmuWriteHalf(Word vAddr, Half data, Bool userMode) {
|
if ((vAddr & 1) != 0) {
|
if ((vAddr & 1) != 0) {
|
/* throw illegal address exception */
|
/* throw illegal address exception */
|
|
mmuBadAccs = MMU_ACCS_WRITE | MMU_ACCS_HALF;
|
mmuBadAddr = vAddr;
|
mmuBadAddr = vAddr;
|
throwException(EXC_ILL_ADDRESS);
|
throwException(EXC_ILL_ADDRESS);
|
}
|
}
|
memoryWriteHalf(v2p(vAddr, userMode, true), data);
|
memoryWriteHalf(v2p(vAddr, userMode, true, MMU_ACCS_HALF), data);
|
}
|
}
|
|
|
|
|
void mmuWriteByte(Word vAddr, Byte data, Bool userMode) {
|
void mmuWriteByte(Word vAddr, Byte data, Bool userMode) {
|
memoryWriteByte(v2p(vAddr, userMode, true), data);
|
memoryWriteByte(v2p(vAddr, userMode, true, MMU_ACCS_BYTE), data);
|
}
|
}
|
|
|
|
|
Word mmuGetIndex(void) {
|
Word mmuGetIndex(void) {
|
return tlbIndex;
|
return tlbIndex;
|
}
|
}
|
|
|
|
|
void mmuSetIndex(Word value) {
|
void mmuSetIndex(Word value) {
|
tlbIndex = value & TLB_MASK;
|
tlbIndex = value & TLB_MASK;
|
}
|
}
|
|
|
|
|
Word mmuGetEntryHi(void) {
|
Word mmuGetEntryHi(void) {
|
return tlbEntryHi;
|
return tlbEntryHi;
|
}
|
}
|
|
|
|
|
void mmuSetEntryHi(Word value) {
|
void mmuSetEntryHi(Word value) {
|
tlbEntryHi = value & PAGE_MASK;
|
tlbEntryHi = value & PAGE_MASK;
|
}
|
}
|
|
|
|
|
Word mmuGetEntryLo(void) {
|
Word mmuGetEntryLo(void) {
|
return tlbEntryLo;
|
return tlbEntryLo;
|
}
|
}
|
|
|
|
|
void mmuSetEntryLo(Word value) {
|
void mmuSetEntryLo(Word value) {
|
tlbEntryLo = value & (PAGE_MASK | TLB_WRITE | TLB_VALID);
|
tlbEntryLo = value & (PAGE_MASK | TLB_WRITE | TLB_VALID);
|
}
|
}
|
|
|
|
|
Word mmuGetBadAddr(void) {
|
Word mmuGetBadAddr(void) {
|
return mmuBadAddr;
|
return mmuBadAddr;
|
}
|
}
|
|
|
|
|
void mmuSetBadAddr(Word value) {
|
void mmuSetBadAddr(Word value) {
|
mmuBadAddr = value;
|
mmuBadAddr = value;
|
}
|
}
|
|
|
|
|
|
Word mmuGetBadAccs(void) {
|
|
return mmuBadAccs;
|
|
}
|
|
|
|
|
|
void mmuSetBadAccs(Word value) {
|
|
mmuBadAccs = value;
|
|
}
|
|
|
|
|
void mmuTbs(void) {
|
void mmuTbs(void) {
|
int index;
|
int index;
|
|
|
index = assoc(tlbEntryHi & PAGE_MASK);
|
index = assoc(tlbEntryHi & PAGE_MASK);
|
if (index == -1) {
|
if (index == -1) {
|
tlbIndex = 0x80000000;
|
tlbIndex = 0x80000000;
|
} else {
|
} else {
|
tlbIndex = index;
|
tlbIndex = index;
|
}
|
}
|
}
|
}
|
|
|
|
|
void mmuTbwr(void) {
|
void mmuTbwr(void) {
|
int index;
|
int index;
|
|
|
/* choose a random index, but don't touch fixed entries */
|
/* choose a random index, but don't touch fixed entries */
|
do {
|
do {
|
index = rand() & TLB_MASK;
|
index = rand() & TLB_MASK;
|
} while (index < TLB_FIXED);
|
} while (index < TLB_FIXED);
|
tlb[index].page = tlbEntryHi & PAGE_MASK;
|
tlb[index].page = tlbEntryHi & PAGE_MASK;
|
tlb[index].frame = tlbEntryLo & PAGE_MASK;
|
tlb[index].frame = tlbEntryLo & PAGE_MASK;
|
tlb[index].write = tlbEntryLo & TLB_WRITE ? true : false;
|
tlb[index].write = tlbEntryLo & TLB_WRITE ? true : false;
|
tlb[index].valid = tlbEntryLo & TLB_VALID ? true : false;
|
tlb[index].valid = tlbEntryLo & TLB_VALID ? true : false;
|
if (debugWrite) {
|
if (debugWrite) {
|
cPrintf("**** TLB[%02d] <- 0x%08X 0x%08X %c %c ****\n",
|
cPrintf("**** TLB[%02d] <- 0x%08X 0x%08X %c %c ****\n",
|
index, tlb[index].page, tlb[index].frame,
|
index, tlb[index].page, tlb[index].frame,
|
tlb[index].write ? 'w' : '-',
|
tlb[index].write ? 'w' : '-',
|
tlb[index].valid ? 'v' : '-');
|
tlb[index].valid ? 'v' : '-');
|
}
|
}
|
}
|
}
|
|
|
|
|
void mmuTbri(void) {
|
void mmuTbri(void) {
|
int index;
|
int index;
|
|
|
index = tlbIndex & TLB_MASK;
|
index = tlbIndex & TLB_MASK;
|
tlbEntryHi = tlb[index].page;
|
tlbEntryHi = tlb[index].page;
|
tlbEntryLo = tlb[index].frame;
|
tlbEntryLo = tlb[index].frame;
|
if (tlb[index].write) {
|
if (tlb[index].write) {
|
tlbEntryLo |= TLB_WRITE;
|
tlbEntryLo |= TLB_WRITE;
|
}
|
}
|
if (tlb[index].valid) {
|
if (tlb[index].valid) {
|
tlbEntryLo |= TLB_VALID;
|
tlbEntryLo |= TLB_VALID;
|
}
|
}
|
}
|
}
|
|
|
|
|
void mmuTbwi(void) {
|
void mmuTbwi(void) {
|
int index;
|
int index;
|
|
|
index = tlbIndex & TLB_MASK;
|
index = tlbIndex & TLB_MASK;
|
tlb[index].page = tlbEntryHi & PAGE_MASK;
|
tlb[index].page = tlbEntryHi & PAGE_MASK;
|
tlb[index].frame = tlbEntryLo & PAGE_MASK;
|
tlb[index].frame = tlbEntryLo & PAGE_MASK;
|
tlb[index].write = tlbEntryLo & TLB_WRITE ? true : false;
|
tlb[index].write = tlbEntryLo & TLB_WRITE ? true : false;
|
tlb[index].valid = tlbEntryLo & TLB_VALID ? true : false;
|
tlb[index].valid = tlbEntryLo & TLB_VALID ? true : false;
|
if (debugWrite) {
|
if (debugWrite) {
|
cPrintf("**** TLB[%02d] <- 0x%08X 0x%08X %c %c ****\n",
|
cPrintf("**** TLB[%02d] <- 0x%08X 0x%08X %c %c ****\n",
|
index, tlb[index].page, tlb[index].frame,
|
index, tlb[index].page, tlb[index].frame,
|
tlb[index].write ? 'w' : '-',
|
tlb[index].write ? 'w' : '-',
|
tlb[index].valid ? 'v' : '-');
|
tlb[index].valid ? 'v' : '-');
|
}
|
}
|
}
|
}
|
|
|
|
|
TLB_Entry mmuGetTLB(int index) {
|
TLB_Entry mmuGetTLB(int index) {
|
return tlb[index & TLB_MASK];
|
return tlb[index & TLB_MASK];
|
}
|
}
|
|
|
|
|
void mmuSetTLB(int index, TLB_Entry tlbEntry) {
|
void mmuSetTLB(int index, TLB_Entry tlbEntry) {
|
index &= TLB_MASK;
|
index &= TLB_MASK;
|
tlb[index] = tlbEntry;
|
tlb[index] = tlbEntry;
|
if (debugWrite) {
|
if (debugWrite) {
|
cPrintf("**** TLB[%02d] <- 0x%08X 0x%08X %c %c ****\n",
|
cPrintf("**** TLB[%02d] <- 0x%08X 0x%08X %c %c ****\n",
|
index, tlb[index].page, tlb[index].frame,
|
index, tlb[index].page, tlb[index].frame,
|
tlb[index].write ? 'w' : '-',
|
tlb[index].write ? 'w' : '-',
|
tlb[index].valid ? 'v' : '-');
|
tlb[index].valid ? 'v' : '-');
|
}
|
}
|
}
|
}
|
|
|
|
|
void mmuReset(void) {
|
void mmuReset(void) {
|
int i;
|
int i;
|
|
|
cPrintf("Resetting MMU...\n");
|
cPrintf("Resetting MMU...\n");
|
for (i = 0; i < TLB_SIZE; i++) {
|
for (i = 0; i < TLB_SIZE; i++) {
|
tlb[i].page = rand() & PAGE_MASK;
|
tlb[i].page = rand() & PAGE_MASK;
|
tlb[i].frame = rand() & PAGE_MASK;
|
tlb[i].frame = rand() & PAGE_MASK;
|
tlb[i].write = rand() & 0x1000 ? true : false;
|
tlb[i].write = rand() & 0x1000 ? true : false;
|
tlb[i].valid = rand() & 0x1000 ? true : false;
|
tlb[i].valid = rand() & 0x1000 ? true : false;
|
if (debugWrite) {
|
if (debugWrite) {
|
cPrintf("**** TLB[%02d] <- 0x%08X 0x%08X %c %c ****\n",
|
cPrintf("**** TLB[%02d] <- 0x%08X 0x%08X %c %c ****\n",
|
i, tlb[i].page, tlb[i].frame,
|
i, tlb[i].page, tlb[i].frame,
|
tlb[i].write ? 'w' : '-',
|
tlb[i].write ? 'w' : '-',
|
tlb[i].valid ? 'v' : '-');
|
tlb[i].valid ? 'v' : '-');
|
}
|
}
|
}
|
}
|
tlbIndex = rand() & TLB_MASK;
|
tlbIndex = rand() & TLB_MASK;
|
tlbEntryHi = rand() & PAGE_MASK;
|
tlbEntryHi = rand() & PAGE_MASK;
|
tlbEntryLo = rand() & (PAGE_MASK | TLB_WRITE | TLB_VALID);
|
tlbEntryLo = rand() & (PAGE_MASK | TLB_WRITE | TLB_VALID);
|
mmuBadAddr = rand();
|
mmuBadAddr = rand();
|
|
mmuBadAccs = rand() & MMU_ACCS_MASK;
|
}
|
}
|
|
|
|
|
void mmuInit(void) {
|
void mmuInit(void) {
|
mmuReset();
|
mmuReset();
|
}
|
}
|
|
|
|
|
void mmuExit(void) {
|
void mmuExit(void) {
|
}
|
}
|
|
|