// license:BSD-3-Clause // copyright-holders:Patrick Mackinlay #ifndef MAME_MACHINE_CAMMU_H #define MAME_MACHINE_CAMMU_H #pragma once #include "cpu/clipper/common.h" #define MCFG_CAMMU_ID(_id) \ downcast(*device).set_cammu_id(_id); #define MCFG_CAMMU_EXCEPTION_CB(_exceptioncb) \ devcb = &downcast(*device).set_exception_callback(DEVCB_##_exceptioncb); #define MCFG_CAMMU_LINK(_tag) \ downcast(*device).add_linked(_tag); class cammu_device : public device_t { public: template devcb_base &set_exception_callback(Object &&cb) { return m_exception_func.set_callback(std::forward(cb)); } static const u32 CAMMU_PAGE_SIZE = 0x1000; static const u32 CAMMU_PAGE_MASK = (CAMMU_PAGE_SIZE - 1); enum pdo_mask : u32 { PDO_MASK = 0xfffff000 }; enum ptde_mask : u32 { PTDE_F = 0x00000001, // page fault PTDE_PTO = 0xfffff000 // page table origin }; enum pte_mask : u32 { PTE_F = 0x00000001, // page fault PTE_R = 0x00000002, // referenced flag PTE_D = 0x00000004, // dirty flag PTE_PL = 0x00000078, // protection level PTE_S = 0x00000180, // system reserved PTE_ST = 0x00000e00, // system tag PTE_RA = 0xfffff000, // real address PTE_CW = 0x00000040, // copy on write (c400) PTE_NDREF = 0x00000080, // secondary reference (software) / copy on write (fault)? PTE_LOCK = 0x00000100 // page lock (software) }; static const int PTE_ST_SHIFT = 9; enum va_mask : u32 { VA_POFS = 0x00000fff, // page offset VA_PTI = 0x003ff000, // page table index VA_PTDI = 0xffc00000 // page table directory index }; enum system_tag_t : u8 { ST0 = 0, // private, write-through, main memory space ST1 = 1, // shared, write-through, main memory space ST2 = 2, // private, copy-back, main memory space ST3 = 3, // noncacheable, main memory space ST4 = 4, // noncacheable, i/o space ST5 = 5, // noncacheable, boot space ST6 = 6, // cache purge ST7 = 7 // slave i/o }; virtual void map(address_map &map) = 0; void set_spaces(std::vector spaces); template std::enable_if_t>::value, bool> load(const u32 ssw, const u32 address, U &&apply) { translated_t t = translate_address(ssw, address, access_size(sizeof(T)), READ); if (t.space != nullptr) { switch (sizeof(T)) { case 1: apply(T(t.space->read_byte(t.address))); break; case 2: apply(T(t.space->read_word(t.address))); break; case 4: apply(T(t.space->read_dword(t.address))); break; case 8: apply(T(t.space->read_qword(t.address))); break; default: fatalerror("unhandled load size %d\n", access_size(sizeof(T))); } return true; } else return false; } template std::enable_if_t::value, bool> store(const u32 ssw, const u32 address, U data) { translated_t t = translate_address(ssw, address, access_size(sizeof(T)), WRITE); if (t.space != nullptr) { switch (sizeof(T)) { case 1: t.space->write_byte(t.address, T(data)); break; case 2: t.space->write_word(t.address, T(data)); break; case 4: t.space->write_dword(t.address, T(data)); break; case 8: t.space->write_qword(t.address, T(data)); break; default: fatalerror("unhandled store size %d\n", access_size(sizeof(T))); } return true; } else return false; } template std::enable_if_t>::value, bool> modify(const u32 ssw, const u32 address, U &&apply) { translated_t t = translate_address(ssw, address, access_size(sizeof(T)), RMW); if (t.space != nullptr) { switch (sizeof(T)) { case 4: t.space->write_dword(t.address, apply(T(t.space->read_dword(t.address)))); break; default: fatalerror("unhandled modify size %d\n", access_size(sizeof(T))); } return true; } else return false; } template std::enable_if_t>::value, bool> fetch(const u32 ssw, const u32 address, U &&apply) { translated_t t = translate_address(ssw, address, access_size(sizeof(T)), EXECUTE); if (t.space != nullptr) { switch (sizeof(T)) { case 2: apply(T(t.space->read_word(t.address))); break; case 4: apply(T(t.space->read_dword_unaligned(t.address))); break; default: fatalerror("unhandled fetch size %d\n", access_size(sizeof(T))); } return true; } else return false; } // address translation for debugger bool memory_translate(const u32 ssw, const int spacenum, const int intention, offs_t &address); protected: cammu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); // device-level overrides virtual void device_start() override; virtual void device_reset() override; enum access_size : u8 { BYTE = 1, WORD = 2, DWORD = 4, QWORD = 8 }; enum access_type : u8 { READ = 1, WRITE = 2, RMW = 3, EXECUTE = 4 }; private: // address translation struct translated_t { address_space *const space; const u32 address; }; translated_t translate_address(const u32 ssw, const u32 virtual_address, const access_size size, const access_type mode); struct pte_t { const u32 entry; const u32 address; }; pte_t get_pte(const u32 va, const bool user); // helpers virtual bool get_access(const access_type mode, const u32 pte, const u32 ssw) const = 0; virtual bool get_alignment() const = 0; virtual u32 get_pdo(const bool user) const = 0; virtual system_tag_t get_ust_space() const = 0; virtual void set_fault_address(const u32 va) = 0; devcb_write16 m_exception_func; address_space *m_space[8]; }; class cammu_c4_device : public cammu_device { public: void set_cammu_id(const u32 cammu_id) { m_control = cammu_id; } DECLARE_READ32_MEMBER(s_pdo_r) { return m_s_pdo; } DECLARE_WRITE32_MEMBER(s_pdo_w) { m_s_pdo = ((m_s_pdo & ~mem_mask) | (data & mem_mask)) & PDO_MASK; } DECLARE_READ32_MEMBER(u_pdo_r) { return m_u_pdo; } DECLARE_WRITE32_MEMBER(u_pdo_w) { m_u_pdo = ((m_u_pdo & ~mem_mask) | (data & mem_mask)) & PDO_MASK; } virtual DECLARE_READ32_MEMBER(control_r) = 0; virtual DECLARE_WRITE32_MEMBER(control_w) = 0; DECLARE_READ32_MEMBER(i_fault_r) { return m_i_fault; } DECLARE_WRITE32_MEMBER(i_fault_w) { m_i_fault = data; } DECLARE_READ32_MEMBER(fault_address_1_r) { return m_fault_address_1; } DECLARE_WRITE32_MEMBER(fault_address_1_w) { m_fault_address_1 = data; } DECLARE_READ32_MEMBER(fault_address_2_r) { return m_fault_address_2; } DECLARE_WRITE32_MEMBER(fault_address_2_w) { m_fault_address_2 = data; } DECLARE_READ32_MEMBER(fault_data_1_lo_r) { return m_fault_data_1_lo; } DECLARE_WRITE32_MEMBER(fault_data_1_lo_w) { m_fault_data_1_lo = data; } DECLARE_READ32_MEMBER(fault_data_1_hi_r) { return m_fault_data_1_hi; } DECLARE_WRITE32_MEMBER(fault_data_1_hi_w) { m_fault_data_1_hi = data; } DECLARE_READ32_MEMBER(fault_data_2_lo_r) { return m_fault_data_2_lo; } DECLARE_WRITE32_MEMBER(fault_data_2_lo_w) { m_fault_data_2_lo = data; } DECLARE_READ32_MEMBER(fault_data_2_hi_r) { return m_fault_data_2_hi; } DECLARE_WRITE32_MEMBER(fault_data_2_hi_w) { m_fault_data_2_hi = data; } protected: cammu_c4_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); virtual void device_start() override; virtual bool get_access(const access_type mode, const u32 pte, const u32 ssw) const override; virtual u32 get_pdo(const bool user) const override { return user ? m_u_pdo : m_s_pdo; } virtual void set_fault_address(const u32 va) override { m_fault_address_1 = va; } u32 m_s_pdo; u32 m_u_pdo; u32 m_control; u32 m_i_fault; u32 m_fault_address_1; u32 m_fault_address_2; u32 m_fault_data_1_lo; u32 m_fault_data_1_hi; u32 m_fault_data_2_lo; u32 m_fault_data_2_hi; }; class cammu_c4t_device : public cammu_c4_device { public: cammu_c4t_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); virtual void map(address_map &map) override; DECLARE_READ32_MEMBER(ram_line_r) { return m_ram_line; } DECLARE_WRITE32_MEMBER(ram_line_w) { m_ram_line = data; } DECLARE_READ32_MEMBER(htlb_offset_r) { return m_htlb_offset; } DECLARE_WRITE32_MEMBER(htlb_offset_w) { m_htlb_offset = data; } DECLARE_READ32_MEMBER(c4_bus_poll_r) { return m_c4_bus_poll; } DECLARE_WRITE32_MEMBER(c4_bus_poll_w) { m_c4_bus_poll = data; } enum control_mask : u32 { CNTL_RUV = 0x00000001, // reset user valid CNTL_RSV = 0x00000002, // reset supervisor valid CNTL_DBWR = 0x00000004, // disable bus watch read CNTL_ATD = 0x00000008, // alignment trap disable CNTL_UST = 0x00000030, // unmapped system tag CNTL_IOTS = 0x00000040, // i/o tag select CNTL_UVS = 0x00000080, // user valid status CNTL_PB = 0x00000100, // purge busy CNTL_CICT = 0x00000200, // clear i-side cache tags CNTL_CFR = 0x00000400, // clear trap registers CNTL_HTLBD = 0x00000800, // htlb disable CNTL_CDCT = 0x00001000, // clear d-side cache tags CNTL_CID = 0xff000000 // cammu id }; enum control_ust_mask : u32 { UST_NCA = 0x00, // unmapped system tag, noncacheable UST_PWT = 0x10, // unmapped system tag, write through UST_PCB = 0x20, // unmapped system tag, copy back UST_PGE = 0x30 // unmapped system tag, purge mode }; enum control_cid_mask : u32 { CID_C4T = 0x00000000 // unknown }; virtual DECLARE_READ32_MEMBER(control_r) override { return m_control; } virtual DECLARE_WRITE32_MEMBER(control_w) override { m_control = ((m_control & (~mem_mask | CNTL_CID)) | (data & (mem_mask & ~CNTL_CID))); } DECLARE_READ32_MEMBER(bio_control_r) { return m_bio_control; } DECLARE_WRITE32_MEMBER(bio_control_w) { m_bio_control = data; } DECLARE_READ32_MEMBER(bio_address_tag_r) { return m_bio_address_tag; } DECLARE_WRITE32_MEMBER(bio_address_tag_w) { m_bio_address_tag = data; } DECLARE_READ32_MEMBER(cache_data_lo_r) { return m_cache_data_lo; } DECLARE_WRITE32_MEMBER(cache_data_lo_w) { m_cache_data_lo = data; } DECLARE_READ32_MEMBER(cache_data_hi_r) { return m_cache_data_hi; } DECLARE_WRITE32_MEMBER(cache_data_hi_w) { m_cache_data_hi = data; } DECLARE_READ32_MEMBER(cache_cpu_tag_r) { return m_cache_cpu_tag; } DECLARE_WRITE32_MEMBER(cache_cpu_tag_w) { m_cache_cpu_tag = data; } DECLARE_READ32_MEMBER(cache_system_tag_valid_r) { return m_cache_system_tag_valid; } DECLARE_WRITE32_MEMBER(cache_system_tag_valid_w) { m_cache_system_tag_valid = data; } DECLARE_READ32_MEMBER(cache_system_tag_r) { return m_cache_system_tag; } DECLARE_WRITE32_MEMBER(cache_system_tag_w) { m_cache_system_tag = data; } DECLARE_READ32_MEMBER(tlb_va_line_r) { return m_tlb_va_line; } DECLARE_WRITE32_MEMBER(tlb_va_line_w) { m_tlb_va_line = data; } DECLARE_READ32_MEMBER(tlb_ra_line_r) { return m_tlb_ra_line; } DECLARE_WRITE32_MEMBER(tlb_ra_line_w) { m_tlb_ra_line = data; } protected: virtual void device_start() override; virtual bool get_alignment() const override { return (m_control & CNTL_ATD) == 0; } virtual system_tag_t get_ust_space() const override { return system_tag_t((m_control & (CNTL_IOTS | CNTL_UST)) >> 4); } private: u32 m_ram_line; u32 m_htlb_offset; u32 m_c4_bus_poll; u32 m_bio_control; u32 m_bio_address_tag; u32 m_cache_data_lo; u32 m_cache_data_hi; u32 m_cache_cpu_tag; u32 m_cache_system_tag_valid; u32 m_cache_system_tag; u32 m_tlb_va_line; u32 m_tlb_ra_line; }; class cammu_c4i_device : public cammu_c4_device { public: cammu_c4i_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); virtual void map(address_map &map) override; enum control_mask : u32 { CNTL_LRAS = 0x00000001, // tlb line replacement CNTL_BWWD = 0x00000002, // buswatch write disable CNTL_BWRD = 0x00000004, // buswatch read disable CNTL_FSR = 0x00000010, // fake system response CNTL_ATD = 0x00000100, // alignment trap disable CNTL_UMM = 0x00003000, // unmapped mode address space select CNTL_POLL = 0x00030000, // poll bus signals CNTL_BM = 0x00040000, // burst mode address space select CNTL_PZBS = 0x00080000, // page 0 boot select CNTL_CRR = 0x00700000, // cache memory refresh rate CNTL_CID = 0xff000000 // cammu identification }; enum control_umm_mask : u32 { UMM_MM = 0x00000000, // mm space, noncacheable UMM_MMRIO = 0x00001000, // mm or i/o space, noncacheable UMM_IO = 0x00002000 // i/o space noncacheable }; enum control_crr_mask : u32 { CRR_GT131 = 0x00000000, // clock rate over 131 MHz CRR_GT66 = 0x00100000, // clock rate over 66 MHz CRR_GT33 = 0x00200000, // clock rate over 33 MHz CRR_GT8 = 0x00300000, // clock rate over 8 MHz CRR_GT2 = 0x00400000, // clock rate over 2 MHz CRR_GT1 = 0x00500000, // clock rate over 1 MHz CRR_GTHALF = 0x00600000, // clock rate over 0.5 MHz CRR_OFF = 0x00700000, // refresh off }; // c4i cammu identification (rev 2 and rev 3 known to have existed) enum control_cid_mask : u32 { CID_C4IR0 = 0x00000000, CID_C4IR2 = 0x02000000 }; virtual DECLARE_READ32_MEMBER(control_r) override { return m_control; } virtual DECLARE_WRITE32_MEMBER(control_w) override { m_control = ((m_control & (~mem_mask | CNTL_CID)) | (data & (mem_mask & ~CNTL_CID))); } enum reset_mask : u32 { RESET_CDCT = 0x00000001, // clear data cache tags RESET_RDUV = 0x00000100, // reset all d-side uv flags RESET_RDSV = 0x00001000, // reset all d-side sv flags RESET_CICT = 0x00010000, // clear ins. cache tags RESET_RIUV = 0x01000000, // reset all i-side uv flags RESET_RISV = 0x10000000, // reset all i-side sv flags RESET_FLUSH = 0x40000000, // flush out burst io buffer RESET_CFR = 0x80000000 // clear fault registers }; DECLARE_READ32_MEMBER(reset_r) { return m_reset; } DECLARE_WRITE32_MEMBER(reset_w) { m_reset = data; } DECLARE_READ32_MEMBER(clr_s_data_tlb_r) { return m_clr_s_data_tlb; } DECLARE_WRITE32_MEMBER(clr_s_data_tlb_w) { m_clr_s_data_tlb = data; } DECLARE_READ32_MEMBER(clr_u_data_tlb_r) { return m_clr_u_data_tlb; } DECLARE_WRITE32_MEMBER(clr_u_data_tlb_w) { m_clr_u_data_tlb = data; } DECLARE_READ32_MEMBER(clr_s_insn_tlb_r) { return m_clr_s_insn_tlb; } DECLARE_WRITE32_MEMBER(clr_s_insn_tlb_w) { m_clr_s_insn_tlb = data; } DECLARE_READ32_MEMBER(clr_u_insn_tlb_r) { return m_clr_u_insn_tlb; } DECLARE_WRITE32_MEMBER(clr_u_insn_tlb_w) { m_clr_u_insn_tlb = data; } DECLARE_READ32_MEMBER(test_data_r) { return m_test_data; } DECLARE_WRITE32_MEMBER(test_data_w) { m_test_data = data; } DECLARE_READ32_MEMBER(test_address_r) { return m_test_address; } DECLARE_WRITE32_MEMBER(test_address_w) { m_test_address = data; } protected: virtual void device_start() override; virtual bool get_alignment() const override { return (m_control & CNTL_ATD) == 0; } // FIXME: don't really know how unmapped mode works on c4i virtual system_tag_t get_ust_space() const override { return (m_control & UMM_IO) ? ST4 : ST3; } private: u32 m_reset; u32 m_clr_s_data_tlb; u32 m_clr_u_data_tlb; u32 m_clr_s_insn_tlb; u32 m_clr_u_insn_tlb; u32 m_test_data; u32 m_test_address; }; class cammu_c3_device : public cammu_device { public: cammu_c3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); virtual void map(address_map &map) override; virtual void map_global(address_map &map); void add_linked(const char *const tag) { m_linked.push_back(downcast(siblingdevice(tag))); } enum control_mask : u32 { CNTL_EP = 0x00000001, // enable prefetch CNTL_EWCW = 0x00000002, // enable watch cpu writes CNTL_EWIW = 0x00000004, // enable watch i/o writes CNTL_EWIR = 0x00000008, // enable watch i/o reads CNTL_UST = 0x00000030, // unmapped system tag CNTL_CV = 0x00000100, // clear valid CNTL_ATE = 0x00000200, // alignment trap enable CNTL_CID = 0xff000000 // cammu id }; enum ust_mask : u32 { UST_0 = 0x00000000, // private, write-through, main memory space UST_1 = 0x00000010, // shared, write-through, main memory space UST_2 = 0x00000020, // private, copy-back, main memory space UST_3 = 0x00000030 // noncacheable, main memory space }; enum control_cid_mask : u32 { CID_C3 = 0x00000000 // unknown }; DECLARE_READ32_MEMBER(s_pdo_r) { return m_s_pdo; } DECLARE_WRITE32_MEMBER(s_pdo_w) { m_s_pdo = ((m_s_pdo & ~mem_mask) | (data & mem_mask)) & PDO_MASK; } DECLARE_READ32_MEMBER(u_pdo_r) { return m_u_pdo; } DECLARE_WRITE32_MEMBER(u_pdo_w) { m_u_pdo = ((m_u_pdo & ~mem_mask) | (data & mem_mask)) & PDO_MASK; } DECLARE_READ32_MEMBER(fault_r) { return m_fault; } DECLARE_WRITE32_MEMBER(fault_w) { m_fault = data; } DECLARE_READ32_MEMBER(control_r) { return m_control; } DECLARE_WRITE32_MEMBER(control_w) { m_control = ((m_control & (~mem_mask | CNTL_CID)) | (data & (mem_mask & ~CNTL_CID))); } DECLARE_READ32_MEMBER(reset_r) { return m_reset; } DECLARE_WRITE32_MEMBER(reset_w) { m_reset = data; } // global methods - relay to each linked device DECLARE_WRITE32_MEMBER(g_s_pdo_w) { for (cammu_c3_device *dev : m_linked) dev->s_pdo_w(space, offset, data, mem_mask); } DECLARE_WRITE32_MEMBER(g_u_pdo_w) { for (cammu_c3_device *dev : m_linked) dev->u_pdo_w(space, offset, data, mem_mask); } DECLARE_WRITE32_MEMBER(g_fault_w) { for (cammu_c3_device *dev : m_linked) dev->fault_w(space, offset, data, mem_mask); } DECLARE_WRITE32_MEMBER(g_control_w) { for (cammu_c3_device *dev : m_linked) dev->control_w(space, offset, data, mem_mask); } DECLARE_WRITE32_MEMBER(g_reset_w) { for (cammu_c3_device *dev : m_linked) dev->reset_w(space, offset, data, mem_mask); } protected: virtual void device_reset() override; virtual void device_start() override; virtual bool get_access(const access_type mode, const u32 pte, const u32 ssw) const override; virtual bool get_alignment() const override { return m_control & CNTL_ATE; } virtual u32 get_pdo(const bool user) const override { return user ? m_u_pdo : m_s_pdo; } virtual system_tag_t get_ust_space() const override { return system_tag_t((m_control & CNTL_UST) >> 4); } virtual void set_fault_address(const u32 va) override { m_fault = va; } private: enum c3_access_t : u8 { N = 0, // no access R = 1, // read permitted W = 2, // write permitted RW = 3, // read and write permitted E = 4, // execute permitted RE = 5, // read and execute permitted RWE = 7 // read, write and execute permitted }; static const u8 i_cammu_column[]; static const u8 d_cammu_column[]; static const c3_access_t cammu_matrix[][16]; u32 m_s_pdo; u32 m_u_pdo; u32 m_fault; u32 m_control; u32 m_reset; std::vector m_linked; }; // device type definitions DECLARE_DEVICE_TYPE(CAMMU_C4T, cammu_c4t_device) DECLARE_DEVICE_TYPE(CAMMU_C4I, cammu_c4i_device) DECLARE_DEVICE_TYPE(CAMMU_C3, cammu_c3_device) #endif // MAME_MACHINE_CAMMU_H