summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/vtlb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/vtlb.cpp')
-rw-r--r--src/devices/cpu/vtlb.cpp310
1 files changed, 310 insertions, 0 deletions
diff --git a/src/devices/cpu/vtlb.cpp b/src/devices/cpu/vtlb.cpp
new file mode 100644
index 00000000000..f5a488100f7
--- /dev/null
+++ b/src/devices/cpu/vtlb.cpp
@@ -0,0 +1,310 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ vtlb.c
+
+ Generic virtual TLB implementation.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "vtlb.h"
+
+
+
+/***************************************************************************
+ DEBUGGING
+***************************************************************************/
+
+#define PRINTF_TLB (0)
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+/* VTLB state */
+struct vtlb_state
+{
+ cpu_device * cpudevice; /* CPU device */
+ address_spacenum space; /* address space */
+ int dynamic; /* number of dynamic entries */
+ int fixed; /* number of fixed entries */
+ int dynindex; /* index of next dynamic entry */
+ int pageshift; /* bits to shift to get page index */
+ int addrwidth; /* logical address bus width */
+ std::vector<offs_t> live; /* array of live entries by table index */
+ std::vector<int> fixedpages; /* number of pages each fixed entry covers */
+ std::vector<vtlb_entry> table; /* table of entries by address */
+};
+
+
+
+/***************************************************************************
+ INITIALIZATION/TEARDOWN
+***************************************************************************/
+
+/*-------------------------------------------------
+ vtlb_alloc - allocate a new VTLB for the
+ given CPU
+-------------------------------------------------*/
+
+vtlb_state *vtlb_alloc(device_t *cpu, address_spacenum space, int fixed_entries, int dynamic_entries)
+{
+ vtlb_state *vtlb;
+
+ /* allocate memory for the core structure */
+ vtlb = auto_alloc_clear(cpu->machine(), vtlb_state);
+
+ /* fill in CPU information */
+ vtlb->cpudevice = downcast<cpu_device *>(cpu);
+ vtlb->space = space;
+ vtlb->dynamic = dynamic_entries;
+ vtlb->fixed = fixed_entries;
+ const address_space_config *spaceconfig = device_get_space_config(*cpu, space);
+ assert(spaceconfig != NULL);
+ vtlb->pageshift = spaceconfig->m_page_shift;
+ vtlb->addrwidth = spaceconfig->m_logaddr_width;
+
+ /* validate CPU information */
+ assert((1 << vtlb->pageshift) > VTLB_FLAGS_MASK);
+ assert(vtlb->addrwidth > vtlb->pageshift);
+
+ /* allocate the entry array */
+ vtlb->live.resize(fixed_entries + dynamic_entries);
+ memset(&vtlb->live[0], 0, vtlb->live.size()*sizeof(vtlb->live[0]));
+ cpu->save_item(NAME(vtlb->live));
+
+ /* allocate the lookup table */
+ vtlb->table.resize((size_t) 1 << (vtlb->addrwidth - vtlb->pageshift));
+ memset(&vtlb->table[0], 0, vtlb->table.size()*sizeof(vtlb->table[0]));
+ cpu->save_item(NAME(vtlb->table));
+
+ /* allocate the fixed page count array */
+ if (fixed_entries > 0)
+ {
+ vtlb->fixedpages.resize(fixed_entries);
+ memset(&vtlb->fixedpages[0], 0, fixed_entries*sizeof(vtlb->fixedpages[0]));
+ cpu->save_item(NAME(vtlb->fixedpages));
+ }
+ return vtlb;
+}
+
+
+/*-------------------------------------------------
+ vtlb_free - free an allocated VTLB
+-------------------------------------------------*/
+
+void vtlb_free(vtlb_state *vtlb)
+{
+ auto_free(vtlb->cpudevice->machine(), vtlb);
+}
+
+
+
+/***************************************************************************
+ FILLING
+***************************************************************************/
+
+/*-------------------------------------------------
+ vtlb_fill - rcalled by the CPU core in
+ response to an unmapped access
+-------------------------------------------------*/
+
+int vtlb_fill(vtlb_state *vtlb, offs_t address, int intention)
+{
+ offs_t tableindex = address >> vtlb->pageshift;
+ vtlb_entry entry = vtlb->table[tableindex];
+ offs_t taddress;
+
+ if (PRINTF_TLB)
+ printf("vtlb_fill: %08X(%X) ... ", address, intention);
+
+ /* should not be called here if the entry is in the table already */
+// assert((entry & (1 << intention)) == 0);
+
+ /* if we have no dynamic entries, we always fail */
+ if (vtlb->dynamic == 0)
+ {
+ if (PRINTF_TLB)
+ printf("failed: no dynamic entries\n");
+ return FALSE;
+ }
+
+ /* ask the CPU core to translate for us */
+ taddress = address;
+ if (!vtlb->cpudevice->translate(vtlb->space, intention, taddress))
+ {
+ if (PRINTF_TLB)
+ printf("failed: no translation\n");
+ return FALSE;
+ }
+
+ /* if this is the first successful translation for this address, allocate a new entry */
+ if ((entry & VTLB_FLAGS_MASK) == 0)
+ {
+ int liveindex = vtlb->dynindex++ % vtlb->dynamic;
+
+ /* if an entry already exists at this index, free it */
+ if (vtlb->live[liveindex] != 0)
+ vtlb->table[vtlb->live[liveindex] - 1] = 0;
+
+ /* claim this new entry */
+ vtlb->live[liveindex] = tableindex + 1;
+
+ /* form a new blank entry */
+ entry = (taddress >> vtlb->pageshift) << vtlb->pageshift;
+ entry |= VTLB_FLAG_VALID;
+
+ if (PRINTF_TLB)
+ printf("success (%08X), new entry\n", taddress);
+ }
+
+ /* otherwise, ensure that different intentions do not produce different addresses */
+ else
+ {
+ assert((entry >> vtlb->pageshift) == (taddress >> vtlb->pageshift));
+ assert(entry & VTLB_FLAG_VALID);
+
+ if (PRINTF_TLB)
+ printf("success (%08X), existing entry\n", taddress);
+ }
+
+ /* add the intention to the list of valid intentions and store */
+ entry |= 1 << (intention & (TRANSLATE_TYPE_MASK | TRANSLATE_USER_MASK));
+ vtlb->table[tableindex] = entry;
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ vtlb_load - load a fixed VTLB entry
+-------------------------------------------------*/
+
+void vtlb_load(vtlb_state *vtlb, int entrynum, int numpages, offs_t address, vtlb_entry value)
+{
+ offs_t tableindex = address >> vtlb->pageshift;
+ int liveindex = vtlb->dynamic + entrynum;
+ int pagenum;
+
+ /* must be in range */
+ assert(entrynum >= 0 && entrynum < vtlb->fixed);
+
+ if (PRINTF_TLB)
+ printf("vtlb_load %d for %d pages at %08X == %08X\n", entrynum, numpages, address, value);
+
+ /* if an entry already exists at this index, free it */
+ if (vtlb->live[liveindex] != 0)
+ {
+ int pagecount = vtlb->fixedpages[entrynum];
+ int oldtableindex = vtlb->live[liveindex] - 1;
+ for (pagenum = 0; pagenum < pagecount; pagenum++)
+ vtlb->table[oldtableindex + pagenum] = 0;
+ }
+
+ /* claim this new entry */
+ vtlb->live[liveindex] = tableindex + 1;
+
+ /* store the raw value, making sure the "fixed" flag is set */
+ value |= VTLB_FLAG_FIXED;
+ vtlb->fixedpages[entrynum] = numpages;
+ for (pagenum = 0; pagenum < numpages; pagenum++)
+ vtlb->table[tableindex + pagenum] = value + (pagenum << vtlb->pageshift);
+}
+
+/*-------------------------------------------------
+ vtlb_dynload - load a dynamic VTLB entry
+-------------------------------------------------*/
+
+void vtlb_dynload(vtlb_state *vtlb, UINT32 index, offs_t address, vtlb_entry value)
+{
+ vtlb_entry entry = vtlb->table[index];
+
+ if (vtlb->dynamic == 0)
+ {
+ if (PRINTF_TLB)
+ printf("failed: no dynamic entries\n");
+ return;
+ }
+
+ int liveindex = vtlb->dynindex++ % vtlb->dynamic;
+ /* is entry already live? */
+ if (!(entry & VTLB_FLAG_VALID))
+ {
+ /* if an entry already exists at this index, free it */
+ if (vtlb->live[liveindex] != 0)
+ vtlb->table[vtlb->live[liveindex] - 1] = 0;
+
+ /* claim this new entry */
+ vtlb->live[liveindex] = index + 1;
+ }
+ /* form a new blank entry */
+ entry = (address >> vtlb->pageshift) << vtlb->pageshift;
+ entry |= VTLB_FLAG_VALID | value;
+
+ if (PRINTF_TLB)
+ printf("success (%08X), new entry\n", address);
+
+ vtlb->table[index] = entry;
+}
+
+/***************************************************************************
+ FLUSHING
+***************************************************************************/
+
+/*-------------------------------------------------
+ vtlb_flush_dynamic - flush all knowledge
+ from the dynamic part of the VTLB
+-------------------------------------------------*/
+
+void vtlb_flush_dynamic(vtlb_state *vtlb)
+{
+ int liveindex;
+
+ if (PRINTF_TLB)
+ printf("vtlb_flush_dynamic\n");
+
+ /* loop over live entries and release them from the table */
+ for (liveindex = 0; liveindex < vtlb->dynamic; liveindex++)
+ if (vtlb->live[liveindex] != 0)
+ {
+ offs_t tableindex = vtlb->live[liveindex] - 1;
+ vtlb->table[tableindex] = 0;
+ vtlb->live[liveindex] = 0;
+ }
+}
+
+
+/*-------------------------------------------------
+ vtlb_flush_address - flush knowledge of a
+ particular address from the VTLB
+-------------------------------------------------*/
+
+void vtlb_flush_address(vtlb_state *vtlb, offs_t address)
+{
+ offs_t tableindex = address >> vtlb->pageshift;
+
+ if (PRINTF_TLB)
+ printf("vtlb_flush_address %08X\n", address);
+
+ /* free the entry in the table; for speed, we leave the entry in the live array */
+ vtlb->table[tableindex] = 0;
+}
+
+
+
+/***************************************************************************
+ ACCESSORS
+***************************************************************************/
+
+/*-------------------------------------------------
+ vtlb_table - return a pointer to the base of
+ the linear VTLB lookup table
+-------------------------------------------------*/
+
+const vtlb_entry *vtlb_table(vtlb_state *vtlb)
+{
+ return &vtlb->table[0];
+}