1 // license:BSD-3-Clause
2 // copyright-holders:Patrick Mackinlay
3 
4 #ifndef MAME_MACHINE_CAMMU_H
5 #define MAME_MACHINE_CAMMU_H
6 
7 #pragma once
8 
9 #include "cpu/clipper/common.h"
10 
11 class cammu_device : public device_t
12 {
13 public:
exception_callback()14 	auto exception_callback() { return m_exception_func.bind(); }
15 
16 	static const u32 CAMMU_PAGE_SIZE = 0x1000;
17 	static const u32 CAMMU_PAGE_MASK = (CAMMU_PAGE_SIZE - 1);
18 
19 	enum pdo_mask : u32
20 	{
21 		PDO_MASK = 0xfffff000
22 	};
23 
24 	enum ptde_mask : u32
25 	{
26 		PTDE_F   = 0x00000001, // page fault
27 		PTDE_PTO = 0xfffff000  // page table origin
28 	};
29 
30 	enum pte_mask : u32
31 	{
32 		PTE_F     = 0x00000001, // page fault
33 		PTE_R     = 0x00000002, // referenced flag
34 		PTE_D     = 0x00000004, // dirty flag
35 		PTE_PL    = 0x00000078, // protection level
36 		PTE_S     = 0x00000180, // system reserved
37 		PTE_ST    = 0x00000e00, // system tag
38 		PTE_RA    = 0xfffff000, // real address
39 
40 		PTE_CW    = 0x00000040, // copy on write (c400)
41 		PTE_NDREF = 0x00000080, // secondary reference (software) / copy on write (fault)?
42 		PTE_LOCK  = 0x00000100  // page lock (software)
43 	};
44 
45 	static constexpr int PL_SHIFT = 3;
46 	static constexpr int ST_SHIFT = 9;
47 
48 	enum va_mask : u32
49 	{
50 		VA_POFS = 0x00000fff, // page offset
51 		VA_PTI  = 0x003ff000, // page table index
52 		VA_PTDI = 0xffc00000  // page table directory index
53 	};
54 
55 	enum system_tag_t : u8
56 	{
57 		ST0 = 0, // private, write-through, main memory space
58 		ST1 = 1, // shared, write-through, main memory space
59 		ST2 = 2, // private, copy-back, main memory space
60 		ST3 = 3, // noncacheable, main memory space
61 		ST4 = 4, // noncacheable, i/o space
62 		ST5 = 5, // noncacheable, boot space
63 		ST6 = 6, // cache purge
64 		ST7 = 7  // slave i/o
65 	};
66 
67 	void set_spaces(address_space &main_space, address_space &io_space, address_space &boot_space);
68 
69 	// translation lookaside buffer and register access
70 	virtual u32 cammu_r(const u32 address) = 0;
71 	virtual void cammu_w(const u32 address, const u32 data) = 0;
72 
load(const u32 ssw,const u32 address,U && apply)73 	template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> load(const u32 ssw, const u32 address, U &&apply)
74 	{
75 		// check for cammu access
76 		if ((ssw & (SSW_UU | SSW_U)) || ((address & ~0x7ff) != 0x00004800))
77 		{
78 			translated_t t = translate_address(ssw, address, access_size(sizeof(T)), READ);
79 
80 			if (!t.cache)
81 				return false;
82 
83 			switch (sizeof(T))
84 			{
85 			case 1: apply(T(t.cache->read_byte(t.address))); break;
86 			case 2: apply(T(t.cache->read_word(t.address))); break;
87 			case 4: apply(T(t.cache->read_dword(t.address))); break;
88 			case 8: apply(T(t.cache->read_qword(t.address))); break;
89 			default:
90 				fatalerror("unhandled load 0x%08x size %d (%s)",
91 					address, access_size(sizeof(T)), machine().describe_context().c_str());
92 			}
93 		}
94 		else if (sizeof(T) == 4)
95 			apply(cammu_r(address));
96 		else
97 			fatalerror("unhandled cammu load 0x%08x size %d (%s)",
98 				address, access_size(sizeof(T)), machine().describe_context().c_str());
99 
100 		return true;
101 	}
102 
store(const u32 ssw,const u32 address,U data)103 	template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, bool> store(const u32 ssw, const u32 address, U data)
104 	{
105 		// check for cammu access
106 		if ((ssw & (SSW_UU | SSW_U)) || ((address & ~0x7ff) != 0x00004800))
107 		{
108 			translated_t t = translate_address(ssw, address, access_size(sizeof(T)), WRITE);
109 
110 			if (!t.cache)
111 				return false;
112 
113 			switch (sizeof(T))
114 			{
115 			case 1: t.cache->write_byte(t.address, T(data)); break;
116 			case 2: t.cache->write_word(t.address, T(data)); break;
117 			case 4: t.cache->write_dword(t.address, T(data)); break;
118 			case 8: t.cache->write_qword(t.address, T(data)); break;
119 			default:
120 				fatalerror("unhandled store 0x%08x size %d (%s)",
121 					address, access_size(sizeof(T)), machine().describe_context().c_str());
122 			}
123 		}
124 		else if (sizeof(T) == 4)
125 			cammu_w(address, data);
126 		else
127 			fatalerror("unhandled cammu store 0x%08x size %d (%s)",
128 				address, access_size(sizeof(T)), machine().describe_context().c_str());
129 
130 		return true;
131 	}
132 
modify(const u32 ssw,const u32 address,U && apply)133 	template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<T(T)>>::value, bool> modify(const u32 ssw, const u32 address, U &&apply)
134 	{
135 		translated_t t = translate_address(ssw, address, access_size(sizeof(T)), access_type(READ | WRITE));
136 
137 		if (!t.cache)
138 			return false;
139 
140 		switch (sizeof(T))
141 		{
142 		case 4: t.cache->write_dword(t.address, apply(T(t.cache->read_dword(t.address)))); break;
143 		default:
144 			fatalerror("unhandled modify 0x%08x size %d (%s)",
145 				address, access_size(sizeof(T)), machine().describe_context().c_str());
146 		}
147 
148 		return true;
149 	}
150 
fetch(const u32 ssw,const u32 address,U && apply)151 	template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> fetch(const u32 ssw, const u32 address, U &&apply)
152 	{
153 		translated_t t = translate_address(ssw, address, access_size(sizeof(T)), EXECUTE);
154 
155 		if (!t.cache)
156 			return false;
157 
158 		switch (sizeof(T))
159 		{
160 		case 2: apply(T(t.cache->read_word(t.address))); break;
161 		case 4:
162 			{
163 				// check for unaligned access
164 				if (address & 0x2)
165 				{
166 					// check for page span
167 					if ((address & CAMMU_PAGE_MASK) == (CAMMU_PAGE_SIZE - 2))
168 					{
169 						translated_t u = translate_address(ssw, address + 2, access_size(sizeof(u16)), EXECUTE);
170 						if (u.cache)
171 						{
172 							const u16 lsw = t.cache->read_word(t.address);
173 							const u16 msw = t.cache->read_word(u.address);
174 
175 							apply((T(msw) << 16) | lsw);
176 						}
177 						else
178 							return false;
179 					}
180 					else
181 						apply(T(t.cache->read_dword_unaligned(t.address)));
182 				}
183 				else
184 					apply(T(t.cache->read_dword(t.address)));
185 			}
186 		break;
187 		default:
188 			fatalerror("unhandled fetch 0x%08x size %d (%s)\n",
189 				address, access_size(sizeof(T)), machine().describe_context().c_str());
190 		}
191 
192 		return true;
193 	}
194 
195 	// address translation for debugger
196 	bool memory_translate(const u32 ssw, const int spacenum, const int intention, offs_t &address);
197 
198 protected:
199 	cammu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
200 
201 	// device-level overrides
202 	virtual void device_start() override;
203 	virtual void device_reset() override;
204 
205 	enum access_size : u8
206 	{
207 		BYTE  = 1,
208 		WORD  = 2,
209 		DWORD = 4,
210 		QWORD = 8
211 	};
212 
213 	enum access_type : u8
214 	{
215 		READ    = 1,
216 		WRITE   = 2,
217 		EXECUTE = 4,
218 
219 		// matrix abbreviations and combinations
220 		N       = 0,
221 		R       = READ,
222 		W       = WRITE,
223 		RW      = READ | WRITE,
224 		RE      = READ | EXECUTE,
225 		RWE     = READ | WRITE | EXECUTE,
226 	};
227 
228 	struct translated_t
229 	{
230 		memory_access<32, 2, 0, ENDIANNESS_LITTLE>::cache *const cache;
231 		const u32 address;
232 	};
233 
234 	struct pte_t
235 	{
236 		u32 entry;
237 		u32 address;
238 	};
239 
240 	struct memory_t
241 	{
242 		address_space *space = nullptr;
243 		memory_access<32, 2, 0, ENDIANNESS_LITTLE>::cache cache;
244 	};
245 
246 	// address translation
247 	virtual translated_t translate_address(const u32 ssw, const u32 virtual_address, const access_size size, const access_type mode);
248 	pte_t get_pte(const u32 va, const bool user);
249 
250 	// helpers
251 	virtual bool get_access(const access_type mode, const u32 pte, const u32 ssw) const = 0;
252 	virtual bool get_alignment() const = 0;
253 	virtual u32 get_pdo(const bool user) const = 0;
254 	virtual system_tag_t get_ust_space() const = 0;
255 	virtual void set_fault(const u32 address, const exception_vector type) = 0;
256 
257 	// device state
258 	devcb_write16 m_exception_func;
259 	memory_t m_memory[8];
260 };
261 
262 class cammu_c4_device : public cammu_device
263 {
264 public:
265 	// TODO: translation lookaside buffer and register access
266 	virtual void map(address_map &map) = 0;
cammu_r(const u32 address)267 	virtual u32 cammu_r(const u32 address) override { return 0; }
cammu_w(const u32 address,const u32 data)268 	virtual void cammu_w(const u32 address, const u32 data) override {}
269 
set_cammu_id(const u32 cammu_id)270 	void set_cammu_id(const u32 cammu_id) { m_control = cammu_id; }
271 
s_pdo_r()272 	u32 s_pdo_r() { return m_s_pdo; }
273 	void s_pdo_w(offs_t offset, u32 data, u32 mem_mask = ~0) { m_s_pdo = ((m_s_pdo & ~mem_mask) | (data & mem_mask)) & PDO_MASK; }
u_pdo_r()274 	u32 u_pdo_r() { return m_u_pdo; }
275 	void u_pdo_w(offs_t offset, u32 data, u32 mem_mask = ~0) { m_u_pdo = ((m_u_pdo & ~mem_mask) | (data & mem_mask)) & PDO_MASK; }
276 
277 	virtual u32 control_r() = 0;
278 	virtual void control_w(offs_t offset, u32 data, u32 mem_mask = ~0) = 0;
279 
i_fault_r()280 	u32 i_fault_r() { return m_i_fault; }
i_fault_w(u32 data)281 	void i_fault_w(u32 data) { m_i_fault = data; }
fault_address_1_r()282 	u32 fault_address_1_r() { return m_fault_address_1; }
fault_address_1_w(u32 data)283 	void fault_address_1_w(u32 data) { m_fault_address_1 = data; }
fault_address_2_r()284 	u32 fault_address_2_r() { return m_fault_address_2; }
fault_address_2_w(u32 data)285 	void fault_address_2_w(u32 data) { m_fault_address_2 = data; }
fault_data_1_lo_r()286 	u32 fault_data_1_lo_r() { return m_fault_data_1_lo; }
fault_data_1_lo_w(u32 data)287 	void fault_data_1_lo_w(u32 data) { m_fault_data_1_lo = data; }
fault_data_1_hi_r()288 	u32 fault_data_1_hi_r() { return m_fault_data_1_hi; }
fault_data_1_hi_w(u32 data)289 	void fault_data_1_hi_w(u32 data) { m_fault_data_1_hi = data; }
fault_data_2_lo_r()290 	u32 fault_data_2_lo_r() { return m_fault_data_2_lo; }
fault_data_2_lo_w(u32 data)291 	void fault_data_2_lo_w(u32 data) { m_fault_data_2_lo = data; }
fault_data_2_hi_r()292 	u32 fault_data_2_hi_r() { return m_fault_data_2_hi; }
fault_data_2_hi_w(u32 data)293 	void fault_data_2_hi_w(u32 data) { m_fault_data_2_hi = data; }
294 
295 protected:
296 	cammu_c4_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
297 
298 	virtual void device_start() override;
299 
300 	virtual bool get_access(const access_type mode, const u32 pte, const u32 ssw) const override;
get_pdo(const bool user)301 	virtual u32 get_pdo(const bool user) const override { return user ? m_u_pdo : m_s_pdo; }
302 
set_fault(const u32 address,const exception_vector type)303 	virtual void set_fault(const u32 address, const exception_vector type) override { m_fault_address_1 = address; m_exception_func(type); }
304 
305 	u32 m_s_pdo;
306 	u32 m_u_pdo;
307 	u32 m_control;
308 
309 	u32 m_i_fault;
310 	u32 m_fault_address_1;
311 	u32 m_fault_address_2;
312 	u32 m_fault_data_1_lo;
313 	u32 m_fault_data_1_hi;
314 	u32 m_fault_data_2_lo;
315 	u32 m_fault_data_2_hi;
316 };
317 
318 class cammu_c4t_device : public cammu_c4_device
319 {
320 public:
321 	cammu_c4t_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
322 
323 	virtual void map(address_map &map) override;
324 
ram_line_r()325 	u32 ram_line_r() { return m_ram_line; }
ram_line_w(u32 data)326 	void ram_line_w(u32 data) { m_ram_line = data; }
327 
htlb_offset_r()328 	u32 htlb_offset_r() { return m_htlb_offset; }
htlb_offset_w(u32 data)329 	void htlb_offset_w(u32 data) { m_htlb_offset = data; }
330 
c4_bus_poll_r()331 	u32 c4_bus_poll_r() { return m_c4_bus_poll; }
c4_bus_poll_w(u32 data)332 	void c4_bus_poll_w(u32 data) { m_c4_bus_poll = data; }
333 
334 	enum control_mask : u32
335 	{
336 		CNTL_RUV   = 0x00000001, // reset user valid
337 		CNTL_RSV   = 0x00000002, // reset supervisor valid
338 		CNTL_DBWR  = 0x00000004, // disable bus watch read
339 		CNTL_ATD   = 0x00000008, // alignment trap disable
340 		CNTL_UST   = 0x00000030, // unmapped system tag
341 		CNTL_IOTS  = 0x00000040, // i/o tag select
342 		CNTL_UVS   = 0x00000080, // user valid status
343 		CNTL_PB    = 0x00000100, // purge busy
344 		CNTL_CICT  = 0x00000200, // clear i-side cache tags
345 		CNTL_CFR   = 0x00000400, // clear trap registers
346 		CNTL_HTLBD = 0x00000800, // htlb disable
347 		CNTL_CDCT  = 0x00001000, // clear d-side cache tags
348 		CNTL_CID   = 0xff000000  // cammu id
349 	};
350 
351 	enum control_ust_mask : u32
352 	{
353 		UST_NCA = 0x00, // unmapped system tag, noncacheable
354 		UST_PWT = 0x10, // unmapped system tag, write through
355 		UST_PCB = 0x20, // unmapped system tag, copy back
356 		UST_PGE = 0x30  // unmapped system tag, purge mode
357 	};
358 
359 	enum control_cid_mask : u32
360 	{
361 		CID_C4T = 0x00000000 // unknown
362 	};
363 
control_r()364 	virtual u32 control_r() override { return m_control; }
365 	virtual void control_w(offs_t offset, u32 data, u32 mem_mask = ~0) override { m_control = ((m_control & (~mem_mask | CNTL_CID)) | (data & (mem_mask & ~CNTL_CID))); }
bio_control_r()366 	u32 bio_control_r() { return m_bio_control; }
bio_control_w(u32 data)367 	void bio_control_w(u32 data) { m_bio_control = data; }
bio_address_tag_r()368 	u32 bio_address_tag_r() { return m_bio_address_tag; }
bio_address_tag_w(u32 data)369 	void bio_address_tag_w(u32 data) { m_bio_address_tag = data; }
370 
cache_data_lo_r()371 	u32 cache_data_lo_r() { return m_cache_data_lo; }
cache_data_lo_w(u32 data)372 	void cache_data_lo_w(u32 data) { m_cache_data_lo = data; }
cache_data_hi_r()373 	u32 cache_data_hi_r() { return m_cache_data_hi; }
cache_data_hi_w(u32 data)374 	void cache_data_hi_w(u32 data) { m_cache_data_hi = data; }
cache_cpu_tag_r()375 	u32 cache_cpu_tag_r() { return m_cache_cpu_tag; }
cache_cpu_tag_w(u32 data)376 	void cache_cpu_tag_w(u32 data) { m_cache_cpu_tag = data; }
cache_system_tag_valid_r()377 	u32 cache_system_tag_valid_r() { return m_cache_system_tag_valid; }
cache_system_tag_valid_w(u32 data)378 	void cache_system_tag_valid_w(u32 data) { m_cache_system_tag_valid = data; }
cache_system_tag_r()379 	u32 cache_system_tag_r() { return m_cache_system_tag; }
cache_system_tag_w(u32 data)380 	void cache_system_tag_w(u32 data) { m_cache_system_tag = data; }
tlb_va_line_r()381 	u32 tlb_va_line_r() { return m_tlb_va_line; }
tlb_va_line_w(u32 data)382 	void tlb_va_line_w(u32 data) { m_tlb_va_line = data; }
tlb_ra_line_r()383 	u32 tlb_ra_line_r() { return m_tlb_ra_line; }
tlb_ra_line_w(u32 data)384 	void tlb_ra_line_w(u32 data) { m_tlb_ra_line = data; }
385 
386 protected:
387 	virtual void device_start() override;
388 
get_alignment()389 	virtual bool get_alignment() const override { return (m_control & CNTL_ATD) == 0; }
get_ust_space()390 	virtual system_tag_t get_ust_space() const override { return system_tag_t((m_control & (CNTL_IOTS | CNTL_UST)) >> 4); }
391 
392 private:
393 	u32 m_ram_line;
394 	u32 m_htlb_offset;
395 	u32 m_c4_bus_poll;
396 	u32 m_bio_control;
397 	u32 m_bio_address_tag;
398 
399 	u32 m_cache_data_lo;
400 	u32 m_cache_data_hi;
401 	u32 m_cache_cpu_tag;
402 	u32 m_cache_system_tag_valid;
403 	u32 m_cache_system_tag;
404 	u32 m_tlb_va_line;
405 	u32 m_tlb_ra_line;
406 };
407 
408 class cammu_c4i_device : public cammu_c4_device
409 {
410 public:
411 	cammu_c4i_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
412 
413 	virtual void map(address_map &map) override;
414 
415 	enum control_mask : u32
416 	{
417 		CNTL_LRAS = 0x00000001, // tlb line replacement
418 		CNTL_BWWD = 0x00000002, // buswatch write disable
419 		CNTL_BWRD = 0x00000004, // buswatch read disable
420 		CNTL_FSR  = 0x00000010, // fake system response
421 		CNTL_ATD  = 0x00000100, // alignment trap disable
422 		CNTL_UMM  = 0x00003000, // unmapped mode address space select
423 		CNTL_POLL = 0x00030000, // poll bus signals
424 		CNTL_BM   = 0x00040000, // burst mode address space select
425 		CNTL_PZBS = 0x00080000, // page 0 boot select
426 		CNTL_CRR  = 0x00700000, // cache memory refresh rate
427 		CNTL_CID  = 0xff000000  // cammu identification
428 	};
429 
430 	enum control_umm_mask : u32
431 	{
432 		UMM_MM    = 0x00000000, // mm space, noncacheable
433 		UMM_MMRIO = 0x00001000, // mm or i/o space, noncacheable
434 		UMM_IO    = 0x00002000  // i/o space noncacheable
435 	};
436 
437 	enum control_crr_mask : u32
438 	{
439 		CRR_GT131  = 0x00000000, // clock rate over 131 MHz
440 		CRR_GT66   = 0x00100000, // clock rate over 66 MHz
441 		CRR_GT33   = 0x00200000, // clock rate over 33 MHz
442 		CRR_GT8    = 0x00300000, // clock rate over 8 MHz
443 		CRR_GT2    = 0x00400000, // clock rate over 2 MHz
444 		CRR_GT1    = 0x00500000, // clock rate over 1 MHz
445 		CRR_GTHALF = 0x00600000, // clock rate over 0.5 MHz
446 		CRR_OFF    = 0x00700000, // refresh off
447 	};
448 
449 	// c4i cammu identification (rev 2 and rev 3 known to have existed)
450 	enum control_cid_mask : u32
451 	{
452 		CID_C4IR0 = 0x00000000,
453 		CID_C4IR2 = 0x02000000
454 	};
455 
control_r()456 	virtual u32 control_r() override { return m_control; }
control_w(offs_t offset,u32 data,u32 mem_mask)457 	virtual void control_w(offs_t offset, u32 data, u32 mem_mask) override { m_control = ((m_control & (~mem_mask | CNTL_CID)) | (data & (mem_mask & ~CNTL_CID))); }
458 
459 	enum reset_mask : u32
460 	{
461 		RESET_CDCT  = 0x00000001, // clear data cache tags
462 		RESET_RDUV  = 0x00000100, // reset all d-side uv flags
463 		RESET_RDSV  = 0x00001000, // reset all d-side sv flags
464 		RESET_CICT  = 0x00010000, // clear ins. cache tags
465 		RESET_RIUV  = 0x01000000, // reset all i-side uv flags
466 		RESET_RISV  = 0x10000000, // reset all i-side sv flags
467 		RESET_FLUSH = 0x40000000, // flush out burst io buffer
468 		RESET_CFR   = 0x80000000  // clear fault registers
469 	};
reset_r()470 	u32 reset_r() { return m_reset; }
reset_w(u32 data)471 	void reset_w(u32 data) { m_reset = data; }
472 
clr_s_data_tlb_r()473 	u32 clr_s_data_tlb_r() { return m_clr_s_data_tlb; }
clr_s_data_tlb_w(u32 data)474 	void clr_s_data_tlb_w(u32 data) { m_clr_s_data_tlb = data; }
clr_u_data_tlb_r()475 	u32 clr_u_data_tlb_r() { return m_clr_u_data_tlb; }
clr_u_data_tlb_w(u32 data)476 	void clr_u_data_tlb_w(u32 data) { m_clr_u_data_tlb = data; }
clr_s_insn_tlb_r()477 	u32 clr_s_insn_tlb_r() { return m_clr_s_insn_tlb; }
clr_s_insn_tlb_w(u32 data)478 	void clr_s_insn_tlb_w(u32 data) { m_clr_s_insn_tlb = data; }
clr_u_insn_tlb_r()479 	u32 clr_u_insn_tlb_r() { return m_clr_u_insn_tlb; }
clr_u_insn_tlb_w(u32 data)480 	void clr_u_insn_tlb_w(u32 data) { m_clr_u_insn_tlb = data; }
481 
test_data_r()482 	u32 test_data_r() { return m_test_data; }
test_data_w(u32 data)483 	void test_data_w(u32 data) { m_test_data = data; }
484 
test_address_r()485 	u32 test_address_r() { return m_test_address; }
test_address_w(u32 data)486 	void test_address_w(u32 data) { m_test_address = data; }
487 
488 protected:
489 	virtual void device_start() override;
490 
get_alignment()491 	virtual bool get_alignment() const override { return (m_control & CNTL_ATD) == 0; }
492 	// FIXME: don't really know how unmapped mode works on c4i
get_ust_space()493 	virtual system_tag_t get_ust_space() const override { return (m_control & UMM_IO) ? ST4 : ST3; }
494 
495 private:
496 	u32 m_reset;
497 	u32 m_clr_s_data_tlb;
498 	u32 m_clr_u_data_tlb;
499 	u32 m_clr_s_insn_tlb;
500 	u32 m_clr_u_insn_tlb;
501 	u32 m_test_data;
502 	u32 m_test_address;
503 };
504 
505 class cammu_c3_device : public cammu_device
506 {
507 public:
508 	cammu_c3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
509 
add_linked(cammu_c3_device * child)510 	void add_linked(cammu_c3_device *child) { m_linked.push_back(child); }
511 
512 protected:
513 	// device-level overrides
514 	virtual void device_reset() override;
515 	virtual void device_start() override;
516 
517 	// translation lookaside buffer and register access
518 	virtual u32 cammu_r(const u32 address) override;
519 	virtual void cammu_w(const u32 address, const u32 data) override;
520 
521 	// address translation
522 	virtual translated_t translate_address(const u32 ssw, const u32 virtual_address, const access_size size, const access_type mode) override;
523 
524 private:
525 	enum cammu_address_mask : u32
526 	{
527 		CAMMU_TLB_VA  = 0x00000001, // tlb va/ra select
528 		CAMMU_TLB_X   = 0x00000002, // tlb x/w line select
529 		CAMMU_TLB_SET = 0x000000fc, // tlb set select
530 		CAMMU_REG     = 0x000000ff, // register select
531 		CAMMU_SELECT  = 0x00000700, // cammu select
532 	};
533 	enum tlb_ra_mask : u32
534 	{
535 		TLB_RA_U  = 0x00000001, // used flag
536 		TLB_RA_R  = 0x00000002, // referenced flag
537 		TLB_RA_D  = 0x00000004, // dirty flag
538 		TLB_RA_PL = 0x00000078, // protection level
539 		TLB_RA_ST = 0x00000e00, // system tag
540 		TLB_RA_RA = 0xfffff000, // real address
541 	};
542 	enum tlb_va_mask : u32
543 	{
544 		TLB_VA_UV = 0x00000002, // user valid flag
545 		TLB_VA_SV = 0x00000004, // supervisor valid flag
546 		TLB_VA_VA = 0xfffc0000, // virtual address tag
547 	};
548 
549 	/*
550 	* The C1/C3 CAMMU has 64-entry, two-way set associative TLB, with lines
551 	* grouped into W and X compartments. The associated U flag is set to
552 	* indicate that the W line of the set was most recently accessed, and
553 	* cleared when the X line was most recently accessed. On TLB miss, the
554 	* least recently used line as indicated by this flag is replaced.
555 	*
556 	* Each line consists of a real address field and a virtual address field.
557 	* The real address field format is practically identical to the page table
558 	* entry format.
559 	*/
560 	struct tlb_line_t
561 	{
562 		u32 ra; // real address field
563 		u32 va; // virtual address field
564 
565 		memory_access<32, 2, 0, ENDIANNESS_LITTLE>::cache cache;
566 	};
567 	struct tlb_set_t
568 	{
569 		tlb_line_t w;
570 		tlb_line_t x;
571 		bool u;
572 	};
573 
574 	enum cammu_select_mask : u32
575 	{
576 		CAMMU_D_TLB = 0x000, // d-cammu tlb
577 		CAMMU_D_REG = 0x100, // d-cammu register
578 		CAMMU_I_TLB = 0x200, // i-cammu tlb
579 		CAMMU_I_REG = 0x300, // i-cammu register
580 		CAMMU_G_TLB = 0x400, // global tlb
581 		CAMMU_G_REG = 0x500, // global register
582 	};
583 	enum cammu_register_mask : u8
584 	{
585 		CAMMU_REG_SPDO    = 0x04, // supervisor pdo register
586 		CAMMU_REG_UPDO    = 0x08, // user pdo register
587 		CAMMU_REG_FAULT   = 0x10, // fault register
588 		CAMMU_REG_CONTROL = 0x40, // control register
589 		CAMMU_REG_RESET   = 0x80, // reset register
590 	};
591 
592 	enum control_mask : u32
593 	{
594 		CNTL_EP   = 0x00000001, // enable prefetch
595 		CNTL_EWCW = 0x00000002, // enable watch cpu writes
596 		CNTL_EWIW = 0x00000004, // enable watch i/o writes
597 		CNTL_EWIR = 0x00000008, // enable watch i/o reads
598 		CNTL_UST  = 0x00000030, // unmapped system tag
599 		CNTL_CV   = 0x00000100, // clear valid
600 		CNTL_ATE  = 0x00000200, // alignment trap enable
601 		CNTL_CID  = 0xff000000  // cammu id
602 	};
603 	enum control_ust_mask : u32
604 	{
605 		UST_0 = 0x00000000, // private, write-through, main memory space
606 		UST_1 = 0x00000010, // shared, write-through, main memory space
607 		UST_2 = 0x00000020, // private, copy-back, main memory space
608 		UST_3 = 0x00000030  // noncacheable, main memory space
609 	};
610 	enum control_cid_mask : u32
611 	{
612 		CID_C3 = 0x00000000 // unknown
613 	};
614 
615 	enum reset_mask : u32
616 	{
617 		RESET_RLVW = 0x00000001, // reset all W line LV flags in cache
618 		RESET_RLVX = 0x00000002, // reset all X line LV flags in cache
619 		RESET_RSV  = 0x00000004, // reset all SV flags in tlb
620 		RESET_RUV  = 0x00000008, // reset all UV flags in tlb
621 		RESET_RD   = 0x00000010, // reset all D flags in tlb
622 		RESET_RR   = 0x00000020, // reset all R flags in tlb
623 		RESET_RU   = 0x00000040, // reset all U flags in cache
624 	};
625 
626 	u32 tlb_r(const u8 address) const;
627 	void tlb_w(const u8 address, const u32 data);
628 	tlb_line_t &tlb_lookup(const bool user, const u32 virtual_address, const access_type mode);
629 
s_pdo_r()630 	u32 s_pdo_r() const { return m_s_pdo; }
s_pdo_w(const u32 data)631 	void s_pdo_w(const u32 data) { m_s_pdo = data & PDO_MASK; }
u_pdo_r()632 	u32 u_pdo_r() const { return m_u_pdo; }
u_pdo_w(const u32 data)633 	void u_pdo_w(const u32 data) { m_u_pdo = data & PDO_MASK; }
fault_r()634 	u32 fault_r() const { return m_fault; }
fault_w(const u32 data)635 	void fault_w(const u32 data) { m_fault = data; }
control_r()636 	u32 control_r() const { return m_control; }
control_w(const u32 data)637 	void control_w(const u32 data) { m_control = (m_control & CNTL_CID) | (data & ~CNTL_CID); }
638 	void reset_w(const u32 data);
639 
get_alignment()640 	virtual bool get_alignment() const override { return m_control & CNTL_ATE; }
get_ust_space()641 	virtual system_tag_t get_ust_space() const override { return system_tag_t((m_control & CNTL_UST) >> 4); }
642 	virtual bool get_access(const access_type mode, const u32 pte, const u32 ssw) const override;
get_pdo(const bool user)643 	virtual u32 get_pdo(const bool user) const override { return user ? m_u_pdo : m_s_pdo; }
644 
set_fault(const u32 address,const exception_vector type)645 	virtual void set_fault(const u32 address, const exception_vector type) override { m_fault = address; m_exception_func(type); }
646 
647 	static const u8 protection_matrix[4][16];
648 
649 	// device state
650 	std::vector<cammu_c3_device *> m_linked;
651 
652 	u32 m_s_pdo;
653 	u32 m_u_pdo;
654 	u32 m_fault;
655 	u32 m_control;
656 
657 	tlb_set_t m_tlb[64];
658 };
659 
660 // device type definitions
661 DECLARE_DEVICE_TYPE(CAMMU_C4T, cammu_c4t_device)
662 DECLARE_DEVICE_TYPE(CAMMU_C4I, cammu_c4i_device)
663 DECLARE_DEVICE_TYPE(CAMMU_C3,  cammu_c3_device)
664 
665 #endif // MAME_MACHINE_CAMMU_H
666