From bc802bb8ac56259b1e305672f63a537c9c6dc87f Mon Sep 17 00:00:00 2001 From: Patrick Mackinlay Date: Fri, 3 Feb 2023 14:15:19 +0700 Subject: mc88200: new device --- scripts/src/machine.lua | 11 + src/devices/machine/mc88200.cpp | 913 ++++++++++++++++++++++++++++++++++++++++ src/devices/machine/mc88200.h | 137 ++++++ 3 files changed, 1061 insertions(+) create mode 100644 src/devices/machine/mc88200.cpp create mode 100644 src/devices/machine/mc88200.h diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 9a7ebb78b69..e3bcc75c45d 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -5113,3 +5113,14 @@ if (MACHINES["MICOMXE1A"]~=null) then MAME_DIR .. "src/devices/machine/micomxe1a.h", } end + +--------------------------------------------------- +-- +--@src/devices/machine/mc88200.h,MACHINES["MC88200"] = true +--------------------------------------------------- +if (MACHINES["MC88200"]~=null) then + files { + MAME_DIR .. "src/devices/machine/mc88200.cpp", + MAME_DIR .. "src/devices/machine/mc88200.h", + } +end diff --git a/src/devices/machine/mc88200.cpp b/src/devices/machine/mc88200.cpp new file mode 100644 index 00000000000..2cfca9138e5 --- /dev/null +++ b/src/devices/machine/mc88200.cpp @@ -0,0 +1,913 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +/* + * Motorola MC88200 Cache/Memory Management Unit (CMMU). + * + * Sources: + * - MC88200 Cache/Memory Management User's Manual, Second Edition (MC88200UM/AD, Rev 1) + * + * TODO: + * - probe commands + * - bus errors + * - mbus snooping + * - multiple cmmus per pbus + * - cycle counting + * - cache inhibited accesses invalidate matching cache tags (no writeback) + * - mc88204 64k variant + * - find out where patc valid flag is stored + */ + +#include "emu.h" + +#include "mc88200.h" + +#define LOG_GENERAL (1U << 0) + +//#define VERBOSE (LOG_GENERAL) +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(MC88200, mc88200_device, "mc88200", "Motorola MC88200 Cache/Memory Management Unit") + +mc88200_device::mc88200_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, u8 id) + : device_t(mconfig, MC88200, tag, owner, clock) + , m_mbus(*this, finder_base::DUMMY_TAG, -1, 32) + , m_id(u32(id) << 24) +{ +} + +enum idr_mask : u32 +{ + IDR_VERSION = 0x001f0000, + IDR_TYPE = 0x00e00000, + IDR_ID = 0xff000000, +}; + +enum idr_type_mask : u32 +{ + TYPE_MC88200 = 0x00a00000, // 16k cache + TYPE_MC88204 = 0x00c00000, // 64k cache +}; + +enum ssr_mask : u32 +{ + SSR_V = 0x00000001, // valid + SSR_BH = 0x00000002, // batc hit + SSR_WP = 0x00000004, // write protection + SSR_U = 0x00000008, // used + SSR_M = 0x00000010, // modified + SSR_CI = 0x00000040, // cache inhibit + SSR_G = 0x00000080, // global + SSR_SP = 0x00000100, // supervisor privilege + SSR_WT = 0x00000200, // writethrough + SSR_BE = 0x00004000, // bus error + SSR_CE = 0x00008000, // copyback error + + SSR_WM = 0x0000c3df, +}; + +enum sctr_mask : u32 +{ + SCTR_PR = 0x00010000, // priority arbitration + SCTR_SE = 0x00020000, // snoop enable + SCTR_PE = 0x00040000, // parity enable +}; + +enum pfsr_mask : u32 +{ + PFSR_OK = 0x00000000, // success (no fault) + PFSR_BE = 0x00030000, // bus error + PFSR_SF = 0x00040000, // segment fault + PFSR_PF = 0x00050000, // page fault + PFSR_SV = 0x00060000, // supervisor violation + PFSR_WV = 0x00070000, // write violation + + PFSR_WM = 0x00070000, +}; + +enum apr_mask : u32 +{ + APR_TE = 0x00000001, // translation enable + APR_CI = 0x00000040, // cache inhibit + APR_G = 0x00000080, // global + APR_WT = 0x00000200, // writethrough + APR_STBA = 0xfffff000, // segment table base address + + APR_WM = 0xfffff2c1, +}; + +enum batc_mask : u32 +{ + BATC_V = 0x00000001, // valid + BATC_WP = 0x00000002, // write protect + BATC_CI = 0x00000004, // cache inhibit + BATC_G = 0x00000008, // global + BATC_WT = 0x00000010, // writethrough + BATC_S = 0x00000020, // supervisor + BATC_PBA = 0x0007ffc0, // physical block address + BATC_LBA = 0xfff80000, // logical block address +}; + +enum cssp_mask : u32 +{ + CSSP_VV0 = 0x00003000, // line valid 0 + CSSP_VV1 = 0x0000c000, // line valid 1 + CSSP_VV2 = 0x00030000, // line valid 2 + CSSP_VV3 = 0x000c0000, // line valid 3 + CSSP_D0 = 0x00100000, // line disable 0 + CSSP_D1 = 0x00200000, // line disable 1 + CSSP_D2 = 0x00400000, // line disable 2 + CSSP_D3 = 0x00800000, // line disable 3 + CSSP_L0 = 0x01000000, // line 1 more recently used than line 0 + CSSP_L1 = 0x02000000, // line 2 more recently used than line 0 + CSSP_L2 = 0x04000000, // line 2 more recently used than line 1 + CSSP_L3 = 0x08000000, // line 3 more recently used than line 0 + CSSP_L4 = 0x10000000, // line 3 more recently used than line 1 + CSSP_L5 = 0x20000000, // line 3 more recently used than line 2 + + CSSP_WM = 0x3ffff000, +}; + +enum valid : unsigned +{ + EXU = 0, // exclusive unmodified + EXM = 1, // exclusive modified + SHU = 2, // shared unmodified + INV = 3, // invalid +}; + +enum patc_mask : u64 +{ + PATC_WP = 0x0000'00000001, // write protect + PATC_M = 0x0000'00000002, // modified + PATC_CI = 0x0000'00000004, // cache inhibit + PATC_G = 0x0000'00000008, // global + PATC_WT = 0x0000'00000010, // writethrough + PATC_S = 0x0000'00000020, // supervisor + PATC_PFA = 0x0000'03ffffc0, // page frame address + PATC_LPA = 0x3fff'fc000000, // logical page address + + PATC_V = 0x8000'00000000, // valid (??) +}; + +enum segment_descriptor_mask : u32 +{ + SGD_V = 0x00000001, // valid + SGD_WP = 0x00000004, // write protect + SGD_CI = 0x00000040, // cache inhibit + SGD_G = 0x00000080, // global + SGD_SP = 0x00000100, // supervisor protection + SGD_WT = 0x00000200, // writethrough + SGD_PTBA = 0xfffff000, // page table base address +}; + +enum page_descriptor_mask : u32 +{ + PGD_V = 0x00000001, // valid + PGD_WP = 0x00000004, // write protect + PGD_U = 0x00000008, // used + PGD_M = 0x00000010, // modified + PGD_CI = 0x00000040, // cache inhibit + PGD_G = 0x00000080, // global + PGD_SP = 0x00000100, // supervisor protection + PGD_WT = 0x00000200, // writethrough + PGD_PFA = 0xfffff000, // page frame address +}; + +enum logical_address_mask : u32 +{ + LA_OFS = 0x00000fff, + LA_PAG = 0x003ff000, + LA_SEG = 0xffc00000, +}; + +void mc88200_device::device_start() +{ + m_cache = std::make_unique(CACHE_SETS); + + save_item(NAME(m_idr)); + save_item(NAME(m_scr)); + save_item(NAME(m_ssr)); + save_item(NAME(m_sar)); + save_item(NAME(m_sctr)); + save_item(NAME(m_pfsr)); + save_item(NAME(m_pfar)); + save_item(NAME(m_sapr)); + save_item(NAME(m_uapr)); + + save_item(NAME(m_batc)); + save_item(NAME(m_patc)); + save_item(NAME(m_patc_ptr)); + + save_item(NAME(m_bus_error)); + + save_pointer(STRUCT_MEMBER(m_cache, status), CACHE_SETS); + // TODO: save state for cache lines + + m_idr = TYPE_MC88200; +} + +void mc88200_device::device_reset() +{ + m_mbus->unmap_readwrite(0xfff00000U | ((m_idr & IDR_ID) >> 12), 0xfff00fffU | ((m_idr & IDR_ID) >> 12)); + + idr_w(m_id); + + m_scr = 0; + m_ssr = 0; + m_sar = 0; // undefined + m_sctr = 0; + m_pfsr = 0; + m_pfar = 0; // undefined + m_sapr = APR_CI; + m_uapr = APR_CI; + + std::fill_n(m_batc, std::size(m_batc), 0U); + + // batc contains two hard-wired entries + m_batc[8] = 0xfff7ffb5; + m_batc[9] = 0xfffffff5; + + std::fill_n(m_patc, std::size(m_patc), 0U); + m_patc_ptr = 0; + + m_bus_error = false; + + m_mbus->install_device(0xfff00000U | ((m_idr & IDR_ID) >> 12), 0xfff00fffU | ((m_idr & IDR_ID) >> 12), *this, &mc88200_device::map); +} + +void mc88200_device::map(address_map &map) +{ + // system interface registers + map(0x000, 0x003).rw(FUNC(mc88200_device::idr_r), FUNC(mc88200_device::idr_w)); + map(0x004, 0x007).rw(FUNC(mc88200_device::scr_r), FUNC(mc88200_device::scr_w)); + map(0x008, 0x00b).rw(FUNC(mc88200_device::ssr_r), FUNC(mc88200_device::ssr_w)); + map(0x00c, 0x00f).rw(FUNC(mc88200_device::sar_r), FUNC(mc88200_device::sar_w)); + map(0x104, 0x107).rw(FUNC(mc88200_device::sctr_r), FUNC(mc88200_device::sctr_w)); + + // p bus fault registers + map(0x108, 0x10b).rw(FUNC(mc88200_device::pfsr_r), FUNC(mc88200_device::pfsr_w)); + map(0x10c, 0x10f).rw(FUNC(mc88200_device::pfar_r), FUNC(mc88200_device::pfar_w)); + + // area pointers + map(0x200, 0x203).rw(FUNC(mc88200_device::sapr_r), FUNC(mc88200_device::sapr_w)); + map(0x204, 0x207).rw(FUNC(mc88200_device::uapr_r), FUNC(mc88200_device::uapr_w)); + + // batc write ports + map(0x400, 0x41f).w(FUNC(mc88200_device::bwp_w)).mirror(0x20); + + // cache diagnostic ports + map(0x800, 0x80f).rw(FUNC(mc88200_device::cdp_r), FUNC(mc88200_device::cdp_w)).mirror(0x30); + map(0x840, 0x84f).rw(FUNC(mc88200_device::ctp_r), FUNC(mc88200_device::ctp_w)).mirror(0x30); + map(0x880, 0x883).rw(FUNC(mc88200_device::cssp_r), FUNC(mc88200_device::cssp_w)).mirror(0x30); +} + +void mc88200_device::idr_w(u32 data) +{ + LOG("idr_w 0x%08x (%s)\n", data, machine().describe_context()); + + if ((data ^ m_idr) & IDR_ID) + { + m_mbus->unmap_readwrite(0xfff00000U | ((m_idr & IDR_ID) >> 12), 0xfff00fffU | ((m_idr & IDR_ID) >> 12)); + + m_idr = (m_idr & ~IDR_ID) | (data & IDR_ID); + + m_mbus->install_device(0xfff00000U | ((m_idr & IDR_ID) >> 12), 0xfff00fffU | ((m_idr & IDR_ID) >> 12), *this, &mc88200_device::map); + } +} + +void mc88200_device::scr_w(u32 data) +{ + LOG("scr_w 0x%08x (%s)\n", data, machine().describe_context()); + + char const *const action[] = { nullptr, "invalidate", "copy back", "copy back and invalidate" }; + + switch (data & 0x3f) + { + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + case 0x10: case 0x11: case 0x12: case 0x13: + LOG("no operation\n"); + break; + case 0x14: // invalidate line + case 0x18: // copy back line + case 0x1c: // copy back and invalidate line + LOG("data cache %s line 0x%08x\n", action[BIT(data, 2, 2)], m_sar); + cache_flush(BIT(m_sar, 4, 8), BIT(m_sar, 4, 8) + 1, &mc88200_device::cache_set::cache_line::match_page, BIT(data, 3), BIT(data, 2)); + break; + case 0x15: // invalidate page + case 0x19: // copy back page + case 0x1d: // copy back and invalidate page + LOG("data cache %s page 0x%08x\n", action[BIT(data, 2, 2)], m_sar & ~LA_OFS); + cache_flush(0, CACHE_SETS, &mc88200_device::cache_set::cache_line::match_page, BIT(data, 3), BIT(data, 2)); + break; + case 0x16: // invalidate segment + case 0x1a: // copy back segment + case 0x1e: // copy back and invalidate segment + LOG("data cache %s segment 0x%08x\n", action[BIT(data, 2, 2)], m_sar & LA_SEG); + cache_flush(0, CACHE_SETS, &mc88200_device::cache_set::cache_line::match_segment, BIT(data, 3), BIT(data, 2)); + break; + case 0x17: // invalidate all + case 0x1b: // copy back all + case 0x1f: // copy back and invalidate all + LOG("data cache %s all\n", action[BIT(data, 2, 2)]); + cache_flush(0, CACHE_SETS, nullptr, BIT(data, 3), BIT(data, 2)); + break; + case 0x20: case 0x21: case 0x22: case 0x23: + case 0x28: case 0x29: case 0x2a: case 0x2b: + logerror("probe user address (unemulated)\n"); + break; + case 0x24: case 0x25: case 0x26: case 0x27: + case 0x2c: case 0x2d: case 0x2e: case 0x2f: + logerror("probe supervisor (unemulated)\n"); + break; + case 0x31: case 0x39: // page + LOG("invalidate PATC descriptors (user, page 0x%08x)\n", m_sar & ~LA_OFS); + for (u64 &patc : m_patc) + if (!(patc & PATC_S) && BIT(patc, 26, 20) == BIT(m_sar, 12, 20)) + patc &= ~PATC_V; + break; + case 0x32: case 0x3a: // segment + LOG("invalidate PATC descriptors (user, segment 0x%08x)\n", m_sar & LA_SEG); + for (u64 &patc : m_patc) + if (!(patc & PATC_S) && BIT(patc, 36, 10) == BIT(m_sar, 22, 10)) + patc &= ~PATC_V; + break; + case 0x33: case 0x3b: // all + LOG("invalidate PATC descriptors (user, all)\n"); + for (u64 &patc : m_patc) + if (!(patc & PATC_S)) + patc &= ~PATC_V; + break; + case 0x35: case 0x3d: // page + LOG("invalidate PATC descriptors (supervisor, page 0x%08x)\n", m_sar & ~LA_OFS); + for (u64 &patc : m_patc) + if ((patc & PATC_S) && BIT(patc, 26, 20) == BIT(m_sar, 12, 20)) + patc &= ~PATC_V; + break; + case 0x36: case 0x3e: // segment + LOG("invalidate PATC descriptors (supervisor, segment 0x%08x)\n", m_sar & LA_SEG); + for (u64 &patc : m_patc) + if ((patc & PATC_S) && BIT(patc, 36, 10) == BIT(m_sar, 22, 10)) + patc &= ~PATC_V; + break; + case 0x37: case 0x3f: // all + LOG("invalidate PATC cache descriptors (supervisor, all)\n"); + for (u64 &patc : m_patc) + if (patc & PATC_S) + patc &= ~PATC_V; + break; + default: + logerror("unknown operation 0x%08x (%s)\n", data, machine().describe_context()); + break; + } + + m_scr = data & 0x3f; +} + +void mc88200_device::ssr_w(u32 data) { LOG("ssr_w 0x%08x (%s)\n", data, machine().describe_context()); m_ssr = data & SSR_WM; } +void mc88200_device::sar_w(u32 data) { LOG("sar_w 0x%08x (%s)\n", data, machine().describe_context()); m_sar = data; } +void mc88200_device::sctr_w(u32 data) { LOG("sctr_w 0x%08x (%s)\n", data, machine().describe_context()); m_sctr = data; } + +void mc88200_device::pfsr_w(u32 data) { LOG("pfsr_w 0x%08x (%s)\n", data, machine().describe_context()); m_pfsr = data & PFSR_WM; } +void mc88200_device::pfar_w(u32 data) { LOG("pfar_w 0x%08x (%s)\n", data, machine().describe_context()); m_pfar = data; } +void mc88200_device::sapr_w(u32 data) { LOG("sapr_w 0x%08x (%s)\n", data, machine().describe_context()); m_sapr = data & APR_WM; } +void mc88200_device::uapr_w(u32 data) { LOG("uapr_w 0x%08x (%s)\n", data, machine().describe_context()); m_uapr = data & APR_WM; } + +void mc88200_device::bwp_w(offs_t offset, u32 data) +{ + LOG("bwp_w %x,0x%08x (%s)\n", offset, data, machine().describe_context()); + + m_batc[offset] = data; +} + +void mc88200_device::cdp_w(offs_t offset, u32 data) +{ + LOG("cdp_w set %d line %d word %d data 0x%08x (%s)\n", + BIT(m_sar, 4, 8), offset, BIT(m_sar, 2, 2), data, machine().describe_context()); + + m_cache[BIT(m_sar, 4, 8)].line[offset].data[BIT(m_sar, 2, 2)] = data; +} + +void mc88200_device::ctp_w(offs_t offset, u32 data) +{ + LOG("ctp_w set %d line %d data 0x%08x (%s)\n", + BIT(m_sar, 4, 8), offset, data, machine().describe_context()); + + m_cache[BIT(m_sar, 4, 8)].line[offset].tag = data & ~LA_OFS; +} + +void mc88200_device::cssp_w(u32 data) +{ + LOG("cssp_w 0x%08x (%s)\n", data, machine().describe_context()); + + m_cache[BIT(m_sar, 4, 8)].status = data & CSSP_WM; +} + +std::optional mc88200_device::translate(u32 virtual_address, bool supervisor, bool write, bool debug) +{ + // select area descriptor + u32 const apr = supervisor ? m_sapr : m_uapr; + if (apr & APR_TE) + { + // check block address translation cache + for (u32 const batc : m_batc) + { + if ((batc & BATC_V) && bool(batc & BATC_S) == supervisor && BIT(virtual_address, 19, 13) == BIT(batc, 19, 13)) + { + if (!write || !(batc & BATC_WP)) + return translate_result(((batc & BATC_PBA) << 13) | (virtual_address & ~BATC_LBA), batc & BATC_CI, batc & BATC_G, batc & BATC_WT); + else + { + // write violation + m_pfsr = PFSR_WV; + + return std::nullopt; + } + } + } + + // check page address translation cache + for (u64 const patc : m_patc) + { + if ((patc & PATC_V) && (bool(patc & PATC_S) == supervisor && BIT(virtual_address, 12, 20) == BIT(patc, 26, 20))) + { + if (!write || !(patc & PATC_WP)) + { + if (!write || (patc & PATC_M)) + return translate_result(((patc & PATC_PFA) << 6) | (virtual_address & LA_OFS), patc & PATC_CI, patc & PATC_G, patc & PATC_WT); + + break; + } + else + { + // write violation + m_pfsr = PFSR_WV; + + return std::nullopt; + } + } + } + + // load and check segment descriptor + u32 const sgd = m_mbus->read_dword((apr & APR_STBA) | ((virtual_address & LA_SEG) >> 20)); + if (m_bus_error) + { + m_pfsr = PFSR_BE; + + return std::nullopt; + } + if (!(sgd & SGD_V)) + { + m_pfsr = PFSR_SF; + m_pfar = (apr & APR_STBA) | ((virtual_address & LA_SEG) >> 20); + + return std::nullopt; + } + if ((sgd & SGD_SP) && !supervisor) + { + m_pfsr = PFSR_SV; + m_pfar = (apr & APR_STBA) | ((virtual_address & LA_SEG) >> 20); + + return std::nullopt; + } + + // load and check page descriptor + u32 pgd = m_mbus->read_dword((sgd & SGD_PTBA) | ((virtual_address & LA_PAG) >> 10)); + if (m_bus_error) + { + m_pfsr = PFSR_BE; + return std::nullopt; + } + if (!(pgd & PGD_V)) + { + m_pfsr = PFSR_PF; + m_pfar = (sgd & SGD_PTBA) | ((virtual_address & LA_PAG) >> 10); + + return std::nullopt; + } + if ((pgd & PGD_SP) && !supervisor) + { + m_pfsr = PFSR_SV; + m_pfar = (sgd & SGD_PTBA) | ((virtual_address & LA_PAG) >> 10); + + return std::nullopt; + } + + // check write protect + if (write && ((sgd | pgd) & PGD_WP)) + { + m_pfsr = PFSR_WV; + return std::nullopt; + } + + if (!debug) + { + // set page descriptor used and modified bits + if (!(pgd & PGD_U) || (write && !(pgd & PGD_M))) + { + pgd |= (write ? PGD_M : 0) | PGD_U; + + m_mbus->write_dword((sgd & SGD_PTBA) | ((virtual_address & LA_PAG) >> 10), pgd); + } + + // create patc entry (lpa,pfa,s,wt,g,ci,m,wp) + m_patc[m_patc_ptr++] = PATC_V | (u64(virtual_address & ~LA_OFS) << 14) | ((pgd & PGD_PFA) >> 6) | (supervisor ? PATC_S : 0) | bitswap(apr | sgd | pgd, 9, 7, 6, 4, 2); + if (m_patc_ptr == std::size(m_patc)) + m_patc_ptr = 0; + } + + return translate_result((pgd & PGD_PFA) | (virtual_address & LA_OFS), (apr | sgd | pgd) & PGD_CI, (apr | sgd | pgd) & PGD_G, (apr | sgd | pgd) & PGD_WT); + } + else + { + /* + * The user manual states that the hardwired BATC entries are used in + * supervisor mode even when translation is disabled. + * + * Despite statements indicating that CMMU control space should be part + * of the supervisor address space, the MVME181 firmware "Cache Inhibit + * Bits" diagnostic expects that CMMU registers also be accessible from + * user mode when translation is disabled. + * + * The following logic assumes that the hardwired entries are applied + * when translation is disabled without regard to the active mode, + * ensuring cache inhibit is activated. + */ + for (unsigned i = 8; i < 10; i++) + if (BIT(virtual_address, 19, 13) == BIT(m_batc[i], 19, 13)) + return translate_result(((m_batc[i] & BATC_PBA) << 13) | (virtual_address & ~BATC_LBA), m_batc[i] & BATC_CI, m_batc[i] & BATC_G, m_batc[i] & BATC_WT); + + return translate_result(virtual_address, apr & APR_CI, apr & APR_G, apr & APR_WT); + } +} + +void mc88200_device::cache_set::set_mru(unsigned const line) +{ + static const struct + { + u32 clr; + u32 set; + } + flags[] = + { + { CSSP_L3 | CSSP_L1 | CSSP_L0, 0 }, + { CSSP_L4 | CSSP_L2, CSSP_L0 }, + { CSSP_L5, CSSP_L2 | CSSP_L1 }, + { 0, CSSP_L5 | CSSP_L4 | CSSP_L3 }, + }; + + status = (status & ~flags[line].clr) | flags[line].set; +} + +void mc88200_device::cache_set::set_unmodified(unsigned const line) +{ + status &= ~(INV << (12 + line * 2)); +} + +void mc88200_device::cache_set::set_modified(unsigned const line) +{ + status &= ~(INV << (12 + line * 2)); + status |= EXM << (12 + line * 2); +} + +void mc88200_device::cache_set::set_shared(unsigned const line) +{ + status &= ~(INV << (12 + line * 2)); + status |= SHU << (12 + line * 2); +} + +void mc88200_device::cache_set::set_invalid(unsigned const line) +{ + status |= INV << (12 + line * 2); +} + +bool mc88200_device::cache_set::modified(unsigned const line) const +{ + return BIT(status, 12 + line * 2, 2) == EXM; +} + +bool mc88200_device::cache_set::shared(unsigned const line) const +{ + return BIT(status, 12 + line * 2, 2) == SHU; +} + +bool mc88200_device::cache_set::invalid(unsigned const line) const +{ + return BIT(status, 12 + line * 2, 2) == INV; +} + +bool mc88200_device::cache_set::enabled(unsigned const line) const +{ + return !BIT(status, 20 + line); +} + +bool mc88200_device::cache_set::cache_line::match_segment(u32 const address) const +{ + return BIT(tag, 22, 10) == BIT(address, 22, 10); +} + +bool mc88200_device::cache_set::cache_line::match_page(u32 const address) const +{ + return BIT(tag, 12, 20) == BIT(address, 12, 20); +} + +void mc88200_device::cache_set::cache_line::load_line(required_address_space bus, u32 const address) +{ + data[0] = bus->read_dword(address | 0x0); + data[1] = bus->read_dword(address | 0x4); + data[2] = bus->read_dword(address | 0x8); + data[3] = bus->read_dword(address | 0xc); +} + +void mc88200_device::cache_set::cache_line::copy_back(required_address_space bus, u32 const address) +{ + bus->write_dword(address | 0x0, data[0]); + bus->write_dword(address | 0x4, data[1]); + bus->write_dword(address | 0x8, data[2]); + bus->write_dword(address | 0xc, data[3]); +} + +std::optional mc88200_device::cache_replace(cache_set const &cs) +{ + // check for enabled invalid lines + for (unsigned l = 0; l < std::size(cs.line); l++) + if (cs.enabled(l) && cs.invalid(l)) + return l; + + /* + * This table encodes the cache line usage sequence for each combination of + * LRU flags. A zero value indicates an invalid flag combination, otherwise + * the two most-significant bits correspond to the most recently-used line + * and the two least-significant bits correspond to the least recently-used. + */ + static u8 const usage_table[] = + { + 0x1b, 0x4b, 0x00, 0x63, 0x27, 0x00, 0x87, 0x93, // 00-07 + 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x9c, // 08-0f + 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x8d, 0x00, // 10-17 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0xb4, // 18-1f + 0x1e, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20-27 + 0x00, 0x72, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, // 28-2f + 0x36, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, // 30-37 + 0xc6, 0xd2, 0x00, 0xd8, 0xc9, 0x00, 0xe1, 0xe4, // 38-3f + }; + + u8 const usage = usage_table[BIT(cs.status, 24, 6)]; + if (usage) + { + // find least-recently used enabled line + for (unsigned i = 0; i < 4; i++) + { + unsigned const l = BIT(usage, i * 2, 2); + + if (cs.enabled(i)) + return l; + } + } + + // invalid flags or no enabled lines + return std::nullopt; +} + +template std::optional mc88200_device::cache_read(u32 physical_address) +{ + unsigned const s = BIT(physical_address, 4, 8); + cache_set &cs = m_cache[s]; + + for (unsigned l = 0; l < std::size(cs.line); l++) + { + // cache line hit: tag match, ensabled, not invalid + if (cs.line[l].match_page(physical_address) && cs.enabled(l) && !cs.invalid(l)) + { + // set most recently used + cs.set_mru(l); + + // return data + u32 const data = cs.line[l].data[BIT(physical_address, 2, 2)]; + + switch (sizeof(T)) + { + case 1: return T(data >> ((physical_address & 3) * 8)); + case 2: return T(data >> ((physical_address & 3) * 8)); + } + + return T(data); + } + } + + // select cache line for replacement + std::optional const l = cache_replace(cs); + + if (l.has_value()) + { + // copy back modified line + if (cs.modified(l.value())) + cs.line[l.value()].copy_back(m_mbus, cs.line[l.value()].tag | (s << 4)); + + // mark line invalid + cs.set_invalid(l.value()); + + // update tag + cs.line[l.value()].tag = physical_address & ~LA_OFS; + + // load line from memory + cs.line[l.value()].load_line(m_mbus, physical_address & 0xfffffff0U); + + // mark line shared unmodified + cs.set_shared(l.value()); + + // set most recently used + cs.set_mru(l.value()); + + // return data + u32 const data = cs.line[l.value()].data[BIT(physical_address, 2, 2)]; + + switch (sizeof(T)) + { + case 1: return T(data >> ((physical_address & 3) * 8)); + case 2: return T(data >> ((physical_address & 3) * 8)); + } + + return T(data); + } + else + { + switch (sizeof(T)) + { + case 1: return m_mbus->read_byte(physical_address); + case 2: return m_mbus->read_word(physical_address); + } + + return m_mbus->read_dword(physical_address); + } +} + +template void mc88200_device::cache_write(u32 physical_address, T data, bool writethrough, bool global) +{ + unsigned const s = BIT(physical_address, 4, 8); + cache_set &cs = m_cache[s]; + + for (unsigned l = 0; l < std::size(cs.line); l++) + { + // cache line hit: tag match, enabled, not invalid + if (cs.line[l].match_page(physical_address) && cs.enabled(l) && !cs.invalid(l)) + { + // update line status + if (cs.shared(l)) + { + if (global) + cs.set_unmodified(l); + else if (!writethrough) + cs.set_modified(l); + } + else + cs.set_modified(l); + + // write data to cache + u32 &cache_data = cs.line[l].data[BIT(physical_address, 2, 2)]; + switch (sizeof(T)) + { + case 1: cache_data = (cache_data & ~(0x000000ffU << ((physical_address & 3) * 8))) | (u32(data) << ((physical_address & 3) * 8)); break; + case 2: cache_data = (cache_data & ~(0x0000ffffU << ((physical_address & 3) * 8))) | (u32(data) << ((physical_address & 3) * 8)); break; + case 4: cache_data = data; break; + } + + // set most recently used + cs.set_mru(l); + + // write data to memory + if (writethrough || global) + { + switch (sizeof(T)) + { + case 1: m_mbus->write_byte(physical_address, data); break; + case 2: m_mbus->write_word(physical_address, data); break; + case 4: m_mbus->write_dword(physical_address, data); break; + } + } + + return; + } + } + + // select cache line for replacement + std::optional const l = cache_replace(cs); + + if (l.has_value()) + { + // copy back modified line + if (cs.modified(l.value())) + cs.line[l.value()].copy_back(m_mbus, cs.line[l.value()].tag | (s << 4)); + + // mark line invalid + cs.set_invalid(l.value()); + + // load line from memory + cs.line[l.value()].load_line(m_mbus, physical_address & 0xfffffff0U); + } + + // write data to memory + switch (sizeof(T)) + { + case 1: m_mbus->write_byte(physical_address, data); break; + case 2: m_mbus->write_word(physical_address, data); break; + case 4: m_mbus->write_dword(physical_address, data); break; + } + + if (l.has_value()) + { + // update tag + cs.line[l.value()].tag = physical_address & ~LA_OFS; + + // write data into cache + u32 &cache_data = cs.line[l.value()].data[BIT(physical_address, 2, 2)]; + switch (sizeof(T)) + { + case 1: cache_data = (cache_data & ~(0x000000ffU << ((physical_address & 3) * 8))) | (u32(data) << ((physical_address & 3) * 8)); break; + case 2: cache_data = (cache_data & ~(0x0000ffffU << ((physical_address & 3) * 8))) | (u32(data) << ((physical_address & 3) * 8)); break; + case 4: cache_data = data; break; + } + + // mark line exclusive unmodified + cs.set_unmodified(l.value()); + + // set most recently used + cs.set_mru(l.value()); + } +} + +void mc88200_device::cache_flush(unsigned const start, unsigned const limit, match_function match, bool const copyback, bool const invalidate) +{ + for (unsigned s = start; s < limit; s++) + { + cache_set &cs = m_cache[s]; + + for (unsigned l = 0; l < std::size(cs.line); l++) + { + if (!match || std::invoke(match, cs.line[l], m_sar)) + { + // copy back + if (copyback && cs.modified(l)) + cs.line[l].copy_back(m_mbus, cs.line[l].tag | (s << 4)); + + // invalidate + if (invalidate) + cs.set_invalid(l); + } + } + } +} + +template std::optional mc88200_device::read(u32 virtual_address, bool supervisor, bool debug) +{ + std::optional result = translate(virtual_address, supervisor, false, debug); + if (!result.has_value()) + return std::nullopt; + + if (result.value().ci) + { + switch (sizeof(T)) + { + case 1: return m_mbus->read_byte(result.value().address); + case 2: return m_mbus->read_word(result.value().address); + case 4: return m_mbus->read_dword(result.value().address); + } + } + else + return cache_read(result.value().address); + + // can't happen + abort(); +} + +template bool mc88200_device::write(u32 virtual_address, T data, bool supervisor, bool debug) +{ + std::optional result = translate(virtual_address, supervisor, true, debug); + if (!result.has_value()) + return false; + + if (result.value().ci) + { + switch (sizeof(T)) + { + case 1: m_mbus->write_byte(result.value().address, data); break; + case 2: m_mbus->write_word(result.value().address, data); break; + case 4: m_mbus->write_dword(result.value().address, data); break; + } + } + else + cache_write(result.value().address, data, result.value().wt, result.value().g); + + return true; +} + +template std::optional mc88200_device::read(u32 virtual_address, bool supervisor, bool debug); +template std::optional mc88200_device::read(u32 virtual_address, bool supervisor, bool debug); +template std::optional mc88200_device::read(u32 virtual_address, bool supervisor, bool debug); + +template bool mc88200_device::write(u32 virtual_address, u8 data, bool supervisor, bool debug); +template bool mc88200_device::write(u32 virtual_address, u16 data, bool supervisor, bool debug); +template bool mc88200_device::write(u32 virtual_address, u32 data, bool supervisor, bool debug); diff --git a/src/devices/machine/mc88200.h b/src/devices/machine/mc88200.h new file mode 100644 index 00000000000..acddea8f6e4 --- /dev/null +++ b/src/devices/machine/mc88200.h @@ -0,0 +1,137 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +#ifndef MAME_MACHINE_MC88200_H +#define MAME_MACHINE_MC88200_H + +#pragma once + +class mc88200_device + : public device_t +{ +public: + mc88200_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, u8 id = 0); + + template void set_mbus(T &&tag, int spacenum) { m_mbus.set_tag(std::forward(tag), spacenum); } + + template std::optional read(u32 virtual_address, bool supervisor, bool debug = false); + template bool write(u32 virtual_address, T data, bool supervisor, bool debug = false); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + + void map(address_map &map); + + u32 idr_r() { return m_idr; } + u32 scr_r() { return m_scr; } + u32 ssr_r() { return m_ssr; } + u32 sar_r() { return m_sar; } + u32 sctr_r() { return m_sctr; } + + u32 pfsr_r() { return m_pfsr; } + u32 pfar_r() { return m_pfar; } + u32 sapr_r() { return m_sapr; } + u32 uapr_r() { return m_uapr; } + + u32 cdp_r(offs_t offset) { return m_cache[BIT(m_sar, 4, 8)].line[offset].data[BIT(m_sar, 2, 2)]; } + u32 ctp_r(offs_t offset) { return m_cache[BIT(m_sar, 4, 8)].line[offset].tag; } + u32 cssp_r() { return m_cache[BIT(m_sar, 4, 8)].status; } + + void idr_w(u32 data); + void scr_w(u32 data); + void ssr_w(u32 data); + void sar_w(u32 data); + void sctr_w(u32 data); + + void pfsr_w(u32 data); + void pfar_w(u32 data); + void sapr_w(u32 data); + void uapr_w(u32 data); + + void bwp_w(offs_t offset, u32 data); + void cdp_w(offs_t offset, u32 data); + void ctp_w(offs_t offset, u32 data); + void cssp_w(u32 data); + + struct translate_result + { + translate_result(u32 address, bool ci, bool g, bool wt) + : address(address) + , ci(ci) + , g(g) + , wt(wt) + {} + + u32 const address; + bool const ci; // cache inhibit + bool const g; // global + bool const wt; // writethrough + }; + std::optional translate(u32 virtual_address, bool supervisor, bool write, bool debug = false); + + struct cache_set + { + void set_mru(unsigned const line); + + void set_unmodified(unsigned const line); + void set_modified(unsigned const line); + void set_shared(unsigned const line); + void set_invalid(unsigned const line); + + bool modified(unsigned const line) const; + bool shared(unsigned const line) const; + bool invalid(unsigned const line) const; + + bool enabled(unsigned const line) const; + + u32 status; + + struct cache_line + { + bool match_segment(u32 const address) const; + bool match_page(u32 const address) const; + + void load_line(required_address_space bus, u32 const address); + void copy_back(required_address_space bus, u32 const address); + + u32 tag; + u32 data[4]; + } + line[4]; + }; + typedef bool (mc88200_device::cache_set::cache_line::* match_function)(u32 const) const; + + std::optional cache_replace(cache_set const &cs); + template std::optional cache_read(u32 physical_address); + template void cache_write(u32 physical_address, T data, bool writethrough, bool global); + void cache_flush(unsigned const start, unsigned const limit, match_function match, bool const copyback, bool const invalidate); + +private: + required_address_space m_mbus; + + u32 m_idr; // identification register + u32 m_scr; // system command register + u32 m_ssr; // system status register + u32 m_sar; // system address register + u32 m_sctr; // system control register + u32 m_pfsr; // p bus fault status register + u32 m_pfar; // p bus fault address register + u32 m_sapr; // supervisor area pointer register + u32 m_uapr; // user area pointer register + + u32 m_batc[10]; // block address translation cache + u64 m_patc[56]; // page address translation cache + u32 m_patc_ptr; + + bool m_bus_error; + + static constexpr unsigned CACHE_SETS = 256; + std::unique_ptr m_cache; // data cache + + u32 const m_id; +}; + +DECLARE_DEVICE_TYPE(MC88200, mc88200_device) + +#endif // MAME_MACHINE_MC88200_H -- cgit v1.2.3