summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/machine/cammu.cpp
blob: cc685b5ecdd9786a55695b25d9a4775dfbde75a4 (plain) (tree)
1
2
3
4
5
6
7


                                      
  

                                                                            
  











                                                                               
  

                                                                                                               

       
                            

                                 



                 
                
                  
 

                    


                             
 

                                                      
 
                                                                                   
















                                                                                                                         
 







                                                                                                                                       
 








                                                                                                                       
 
                                                                                                         
 









                                                                                                                         
 








                                                                                                       
 








                                                                      
 


                                                                       

                                                                                                                   
                                                                

 

                                                                                                                   
                                                                


 
                                                                                                                                   
                                                        

 

                                                                                                                 
                                                            

                           

 
 

                                                                                                                             



                                 

                                 
                                   





                                 
                                    
 












                                           

 
                                     
 









                                          
 

















                                                  
 


                                     
 





                                   
 


                                     
 


                                                                                                              
 
                                                                  
 
                                                                                           
 
                                                                     
 
 



                                                                                                            

                                                                              












                                                                                    
                                                                                                                                                    
 

                                                                                 
 
                                     
                                                                  




                                                                                                                      
 


                                              
 

                                                                                         
         
                                                 
                 
                                                          



                                                                               
 
                                                         

                                                                               
 
                                                          

                                                                               
                 
         
 

                                                         
                                                                     
 
                                   
                                                   
 
                                
                              
         
                                                       

                                                                                        

                                                                                               

                                                           
                                                                                                            

                 
                                      
         
 
                                            
                                                                                    
         
                                                                                      

                                                                                       


                                                   

                                                                             
                                                         
 
                                      
         
 




                                                                                  
 


                                                                                                      
 

                                                                                                                                                     

 
                                                          
                                                                        
 

                                                                               
 

                                                                             
 

                                                              
 
                                                                                            
 


                                                     
 

                                                                        
 

                                                                                  
 

                                                                       
 
                                                                                              
 


                                                                                                                            
 
                   
 
 
                                                                                            


                     



                                              

         
                     

 
                                                                                            
 

                                          
 
                                                                                                   
                                                                    
 

                     



                                                       
         
 
                     
 











                                                                                                
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * An implementation of the Fairchild/Intergraph Cache and Memory Management
 * Unit (CAMMU) designed for use with the CLIPPER CPU family.
 *
 * The C100 and C300 designs used a pair of identical CAMMU devices, each
 * containing a cache, TLB and dynamic translation unit. One device was
 * configured and used for instruction memory, the other for data. It is
 * possible to write to multiple CAMMU devices sharing a common system bus by
 * using "global" register addresses.
 *
 * C400 designs initially implemented the memory management and cache functions
 * using discrete logic, later using a more highly integrated memory management
 * implementation, but still using discrete cache memory. In these systems, the
 * mmu is consolidated into a single logical unit handling both instruction and
 * data memory, with distinctly different program-visible architectures on the
 * C4I and C4E/T devices. Almost no documentation for these has been located.
 *
 * Primary reference: http://bitsavers.org/pdf/fairchild/clipper/CLIPPER%20C300%2032-Bit%20Compute%20Engine.pdf
 * Another reference: http://www.eecs.berkeley.edu/Pubs/TechRpts/1986/CSD-86-289.pdf
 *
 * TODO
 *   - fault register values
 *   - c3 protection faults
 *   - hard-wired and dynamic tlb
 *   - cache
 *   - bus errors
 */

#include "emu.h"
#include "cammu.h"

#include <algorithm>

#define LOG_GENERAL (1U << 0)
#define LOG_ACCESS  (1U << 1)
#define LOG_DTU     (1U << 2)

//#define VERBOSE (LOG_GENERAL | LOG_ACCESS | LOG_DTU)
#include "logmacro.h"

// each variant of the cammu has different registers and a different addressing map
void cammu_c4t_device::map(address_map &map)
{
	map(0x008, 0x00b).rw(this, FUNC(cammu_c4t_device::ram_line_r), FUNC(cammu_c4t_device::ram_line_w));
	map(0x010, 0x013).rw(this, FUNC(cammu_c4t_device::s_pdo_r), FUNC(cammu_c4t_device::s_pdo_w));
	map(0x018, 0x01b).rw(this, FUNC(cammu_c4t_device::u_pdo_r), FUNC(cammu_c4t_device::u_pdo_w));
	map(0x020, 0x023).rw(this, FUNC(cammu_c4t_device::htlb_offset_r), FUNC(cammu_c4t_device::htlb_offset_w));
	map(0x028, 0x02b).rw(this, FUNC(cammu_c4t_device::i_fault_r), FUNC(cammu_c4t_device::i_fault_w));
	map(0x030, 0x033).rw(this, FUNC(cammu_c4t_device::fault_address_1_r), FUNC(cammu_c4t_device::fault_address_1_w));
	map(0x038, 0x03b).rw(this, FUNC(cammu_c4t_device::fault_address_2_r), FUNC(cammu_c4t_device::fault_address_2_w));
	map(0x040, 0x043).rw(this, FUNC(cammu_c4t_device::fault_data_1_lo_r), FUNC(cammu_c4t_device::fault_data_1_lo_w));
	map(0x048, 0x04b).rw(this, FUNC(cammu_c4t_device::fault_data_1_hi_r), FUNC(cammu_c4t_device::fault_data_1_hi_w));
	map(0x050, 0x053).rw(this, FUNC(cammu_c4t_device::fault_data_2_lo_r), FUNC(cammu_c4t_device::fault_data_2_lo_w));
	map(0x058, 0x05b).rw(this, FUNC(cammu_c4t_device::fault_data_2_hi_r), FUNC(cammu_c4t_device::fault_data_2_hi_w));
	map(0x060, 0x063).rw(this, FUNC(cammu_c4t_device::c4_bus_poll_r), FUNC(cammu_c4t_device::c4_bus_poll_w));
	map(0x068, 0x06b).rw(this, FUNC(cammu_c4t_device::control_r), FUNC(cammu_c4t_device::control_w));
	map(0x070, 0x073).rw(this, FUNC(cammu_c4t_device::bio_control_r), FUNC(cammu_c4t_device::bio_control_w));
	map(0x078, 0x07b).rw(this, FUNC(cammu_c4t_device::bio_address_tag_r), FUNC(cammu_c4t_device::bio_address_tag_w));

	map(0x100, 0x103).rw(this, FUNC(cammu_c4t_device::cache_data_lo_r), FUNC(cammu_c4t_device::cache_data_lo_w));
	map(0x104, 0x107).rw(this, FUNC(cammu_c4t_device::cache_data_hi_r), FUNC(cammu_c4t_device::cache_data_hi_w));
	map(0x108, 0x10b).rw(this, FUNC(cammu_c4t_device::cache_cpu_tag_r), FUNC(cammu_c4t_device::cache_cpu_tag_w));
	map(0x10c, 0x10f).rw(this, FUNC(cammu_c4t_device::cache_system_tag_valid_r), FUNC(cammu_c4t_device::cache_system_tag_valid_w));
	map(0x110, 0x113).rw(this, FUNC(cammu_c4t_device::cache_system_tag_r), FUNC(cammu_c4t_device::cache_system_tag_w));
	map(0x118, 0x11b).rw(this, FUNC(cammu_c4t_device::tlb_va_line_r), FUNC(cammu_c4t_device::tlb_va_line_w));
	map(0x11c, 0x11f).rw(this, FUNC(cammu_c4t_device::tlb_ra_line_r), FUNC(cammu_c4t_device::tlb_ra_line_w));
}

void cammu_c4i_device::map(address_map &map)
{
	map(0x000, 0x003).rw(this, FUNC(cammu_c4i_device::reset_r), FUNC(cammu_c4i_device::reset_w));
	map(0x010, 0x013).rw(this, FUNC(cammu_c4i_device::s_pdo_r), FUNC(cammu_c4i_device::s_pdo_w));
	map(0x018, 0x01b).rw(this, FUNC(cammu_c4i_device::u_pdo_r), FUNC(cammu_c4i_device::u_pdo_w));
	map(0x020, 0x023).rw(this, FUNC(cammu_c4i_device::clr_s_data_tlb_r), FUNC(cammu_c4i_device::clr_s_data_tlb_w));
	map(0x028, 0x02b).rw(this, FUNC(cammu_c4i_device::clr_u_data_tlb_r), FUNC(cammu_c4i_device::clr_u_data_tlb_w));
	map(0x030, 0x033).rw(this, FUNC(cammu_c4i_device::clr_s_insn_tlb_r), FUNC(cammu_c4i_device::clr_s_insn_tlb_w));
	map(0x038, 0x03b).rw(this, FUNC(cammu_c4i_device::clr_u_insn_tlb_r), FUNC(cammu_c4i_device::clr_u_insn_tlb_w));

	map(0x068, 0x06b).rw(this, FUNC(cammu_c4i_device::control_r), FUNC(cammu_c4i_device::control_w));

	map(0x080, 0x083).rw(this, FUNC(cammu_c4i_device::test_data_r), FUNC(cammu_c4i_device::test_data_w));
	map(0x088, 0x08b).rw(this, FUNC(cammu_c4i_device::i_fault_r), FUNC(cammu_c4i_device::i_fault_w));
	map(0x090, 0x093).rw(this, FUNC(cammu_c4i_device::fault_address_1_r), FUNC(cammu_c4i_device::fault_address_1_w));
	map(0x098, 0x09b).rw(this, FUNC(cammu_c4i_device::fault_address_2_r), FUNC(cammu_c4i_device::fault_address_2_w));
	map(0x0a0, 0x0a3).rw(this, FUNC(cammu_c4i_device::fault_data_1_lo_r), FUNC(cammu_c4i_device::fault_data_1_lo_w));
	map(0x0a8, 0x0ab).rw(this, FUNC(cammu_c4i_device::fault_data_1_hi_r), FUNC(cammu_c4i_device::fault_data_1_hi_w));
	map(0x0b0, 0x0b3).rw(this, FUNC(cammu_c4i_device::fault_data_2_lo_r), FUNC(cammu_c4i_device::fault_data_2_lo_w));
	map(0x0b8, 0x0bb).rw(this, FUNC(cammu_c4i_device::fault_data_2_hi_r), FUNC(cammu_c4i_device::fault_data_2_hi_w));
	map(0x0c0, 0x0c3).rw(this, FUNC(cammu_c4i_device::test_address_r), FUNC(cammu_c4i_device::test_address_w));
}

void cammu_c3_device::map(address_map &map)
{
	map(0x000, 0x0ff).noprw(); // tlb
	map(0x104, 0x107).rw(this, FUNC(cammu_c3_device::s_pdo_r), FUNC(cammu_c3_device::s_pdo_w));
	map(0x108, 0x10b).rw(this, FUNC(cammu_c3_device::u_pdo_r), FUNC(cammu_c3_device::u_pdo_w));
	map(0x110, 0x113).rw(this, FUNC(cammu_c3_device::fault_r), FUNC(cammu_c3_device::fault_w));
	map(0x140, 0x143).rw(this, FUNC(cammu_c3_device::control_r), FUNC(cammu_c3_device::control_w));
	map(0x180, 0x183).rw(this, FUNC(cammu_c3_device::reset_r), FUNC(cammu_c3_device::reset_w));
}

void cammu_c3_device::map_global(address_map &map)
{
	map(0x000, 0x0ff).noprw(); // global tlb
	map(0x104, 0x107).w(this, FUNC(cammu_c3_device::g_s_pdo_w));
	map(0x108, 0x10b).w(this, FUNC(cammu_c3_device::g_u_pdo_w));
	map(0x110, 0x113).w(this, FUNC(cammu_c3_device::g_fault_w));
	map(0x140, 0x143).w(this, FUNC(cammu_c3_device::g_control_w));
	map(0x180, 0x183).w(this, FUNC(cammu_c3_device::g_reset_w));
}

DEFINE_DEVICE_TYPE(CAMMU_C4T, cammu_c4t_device, "c4t", "C4E/C4T CAMMU")
DEFINE_DEVICE_TYPE(CAMMU_C4I, cammu_c4i_device, "c4i", "C4I CAMMU")
DEFINE_DEVICE_TYPE(CAMMU_C3,  cammu_c3_device,  "c3",  "C1/C3 CAMMU")

cammu_c4t_device::cammu_c4t_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: cammu_c4_device(mconfig, CAMMU_C4T, tag, owner, clock)
{
}

cammu_c4i_device::cammu_c4i_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: cammu_c4_device(mconfig, CAMMU_C4I, tag, owner, clock)
{
}

cammu_c4_device::cammu_c4_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
	: cammu_device(mconfig, type, tag, owner, clock)
{
}

cammu_c3_device::cammu_c3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: cammu_device(mconfig, CAMMU_C3, tag, owner, clock)
	, m_control(CID_C3)
	, m_linked{ this }
{
}

cammu_device::cammu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, type, tag, owner, clock)
	, m_exception_func(*this)
{
}

void cammu_device::device_start()
{
	m_exception_func.resolve();
}

void cammu_device::device_reset()
{
}

void cammu_c4_device::device_start()
{
	cammu_device::device_start();

	save_item(NAME(m_s_pdo));
	save_item(NAME(m_u_pdo));
	save_item(NAME(m_control));

	save_item(NAME(m_i_fault));
	save_item(NAME(m_fault_address_1));
	save_item(NAME(m_fault_address_2));
	save_item(NAME(m_fault_data_1_lo));
	save_item(NAME(m_fault_data_1_hi));
	save_item(NAME(m_fault_data_2_lo));
	save_item(NAME(m_fault_data_2_hi));
}

void cammu_c4i_device::device_start()
{
	cammu_c4_device::device_start();

	save_item(NAME(m_reset));
	save_item(NAME(m_clr_s_data_tlb));
	save_item(NAME(m_clr_u_data_tlb));
	save_item(NAME(m_clr_s_insn_tlb));
	save_item(NAME(m_clr_u_insn_tlb));
	save_item(NAME(m_test_data));
	save_item(NAME(m_test_address));
}

void cammu_c4t_device::device_start()
{
	cammu_c4_device::device_start();

	save_item(NAME(m_ram_line));
	save_item(NAME(m_htlb_offset));
	save_item(NAME(m_c4_bus_poll));
	save_item(NAME(m_bio_control));
	save_item(NAME(m_bio_address_tag));

	save_item(NAME(m_cache_data_lo));
	save_item(NAME(m_cache_data_hi));
	save_item(NAME(m_cache_cpu_tag));
	save_item(NAME(m_cache_system_tag_valid));
	save_item(NAME(m_cache_system_tag));
	save_item(NAME(m_tlb_va_line));
	save_item(NAME(m_tlb_ra_line));
}

void cammu_c3_device::device_start()
{
	cammu_device::device_start();

	save_item(NAME(m_s_pdo));
	save_item(NAME(m_u_pdo));
	save_item(NAME(m_fault));
	save_item(NAME(m_control));
	save_item(NAME(m_reset));
}

void cammu_c3_device::device_reset()
{
	cammu_device::device_reset();

	m_control = (m_control & CNTL_CID) | (CNTL_ATE | UST_3 | CNTL_EWIR | CNTL_EWIW | CNTL_EWCW | CNTL_EP);
	m_reset = 0;
}

void cammu_device::set_spaces(std::vector<address_space *> spaces)
{
	assert_always(spaces.size() == 8, "exactly 8 address space pointers are required");

	std::copy(spaces.begin(), spaces.end(), std::begin(m_space));
}

bool cammu_device::memory_translate(const u32 ssw, const int spacenum, const int intention, offs_t &address)
{
	// translate the address
	translated_t translated = translate_address(ssw, address, BYTE,
		(intention & TRANSLATE_TYPE_MASK) == TRANSLATE_READ ? READ :
		(intention & TRANSLATE_TYPE_MASK) == TRANSLATE_WRITE ? WRITE :
		EXECUTE);

	// check that the requested space number matches the mapped space
	if (translated.space != nullptr && translated.space->spacenum() == spacenum)
	{
		address = translated.address;

		return true;
	}

	return false;
}

cammu_device::translated_t cammu_device::translate_address(const u32 ssw, const u32 virtual_address, const access_size size, const access_type mode)
{
	// get effective user/supervisor mode
	const bool user = mode == EXECUTE ? ssw & SSW_U : ssw & (SSW_U | SSW_UU);

	// check for alignment faults
	if (!machine().side_effects_disabled() && get_alignment())
	{
		if ((mode == EXECUTE && (virtual_address & 0x1)) || (mode != EXECUTE && virtual_address & (size - 1)))
		{
			set_fault_address(virtual_address);
			m_exception_func(mode == EXECUTE ? EXCEPTION_I_ALIGNMENT_FAULT : EXCEPTION_D_ALIGNMENT_FAULT);

			return { nullptr, 0 };
		}
	}

	// in supervisor mode, the first 8 pages are always mapped via the hard-wired tlb
	if (!user && (virtual_address & ~0x7fff) == 0)
	{
		switch (virtual_address & 0x7000)
		{
			// pages 0-3: main space pages 0-3
		case 0x0000: return { m_space[ST1], virtual_address & 0x3fff };
		case 0x1000: return { m_space[ST2], virtual_address & 0x3fff };
		case 0x2000: return { m_space[ST3], virtual_address & 0x3fff };
		case 0x3000: return { m_space[ST3], virtual_address & 0x3fff };

			// pages 4-5: i/o space pages 0-1
		case 0x4000: return { m_space[ST4], virtual_address & 0x1fff };
		case 0x5000: return { m_space[ST4], virtual_address & 0x1fff };

			// pages 6-7: boot space pages 0-1
		case 0x6000: return { m_space[ST5], virtual_address & 0x1fff };
		case 0x7000: return { m_space[ST5], virtual_address & 0x1fff };
		}
	}

	// if not in mapped mode, use unmapped system tag
	if ((ssw & SSW_M) == 0)
		return { m_space[get_ust_space()], virtual_address };

	// get the page table entry
	pte_t pte = get_pte(virtual_address, user);

	// check for page faults
	if (pte.entry & PTE_F)
	{
		if (!machine().side_effects_disabled())
		{
			LOG("%s page fault address 0x%08x ssw 0x%08x pte 0x%08x (%s)\n",
				mode == EXECUTE ? "instruction" : "data",
				virtual_address, ssw, pte.entry, machine().describe_context());

			set_fault_address(virtual_address);
			m_exception_func(mode == EXECUTE ? EXCEPTION_I_PAGE_FAULT : EXCEPTION_D_PAGE_FAULT);
		}

		return { nullptr, 0 };
	}

	// check for protection level faults
	if (!machine().side_effects_disabled() && !get_access(mode, pte.entry, ssw))
	{
		LOG("%s protection fault address 0x%08x ssw 0x%08x pte 0x%08x (%s)\n",
			mode == EXECUTE ? "execute" : mode == READ ? "read" : "write",
			virtual_address, ssw, pte.entry, machine().describe_context());

		set_fault_address(virtual_address);
		m_exception_func(
			mode == EXECUTE ? EXCEPTION_I_EXECUTE_PROTECT_FAULT :
			mode == READ ? EXCEPTION_D_READ_PROTECT_FAULT :
			EXCEPTION_D_WRITE_PROTECT_FAULT);

		return { nullptr, 0 };
	}

	// set pte referenced and dirty flags
	if (mode & WRITE && !(pte.entry & PTE_D))
		m_space[ST0]->write_dword(pte.address, pte.entry | PTE_D | PTE_R);
	else if (!(pte.entry & PTE_R))
		m_space[ST0]->write_dword(pte.address, pte.entry | PTE_R);

	// translate the address
	LOGMASKED(LOG_DTU, "%s address translated 0x%08x\n", mode == EXECUTE ? "instruction" : "data",
		(pte.entry & ~CAMMU_PAGE_MASK) | (virtual_address & CAMMU_PAGE_MASK));

	// return the system tag and translated address
	return { m_space[system_tag_t((pte.entry & PTE_ST) >> PTE_ST_SHIFT)], (pte.entry & ~CAMMU_PAGE_MASK) | (virtual_address & CAMMU_PAGE_MASK) };
}

// return the page table entry for a given virtual address
cammu_device::pte_t cammu_device::get_pte(const u32 va, const bool user)
{
	// get page table directory origin from user or supervisor pdo register
	const u32 pdo = get_pdo(user);

	// get page table directory index from top 12 bits of virtual address
	const u32 ptdi = (va & VA_PTDI) >> 20;

	// fetch page table directory entry
	const u32 ptde = m_space[ST0]->read_dword(pdo | ptdi);

	LOGMASKED(LOG_DTU, "get_pte pdo 0x%08x ptdi 0x%08x ptde 0x%08x\n", pdo, ptdi, ptde);

	// check for page table directory entry fault
	if (ptde & PTDE_F)
		return { PTE_F, pdo | ptdi };

	// get the page table origin from the page table directory entry
	const u32 pto = ptde & PTDE_PTO;

	// get the page table index from the middle 12 bits of the virtual address
	const u32 pti = (va & VA_PTI) >> 10;

	// fetch page table entry
	pte_t pte = { m_space[ST0]->read_dword(pto | pti), pto | pti };

	LOGMASKED(LOG_DTU, "get_pte pto 0x%08x pti 0x%08x pte 0x%08x\n", pto, pti, pte.entry);

	// check for page table entry fault
	if (!(pte.entry & PTE_F))
		LOGMASKED(LOG_DTU, "get_pte address 0x%08x pte 0x%08x (%s)\n", va, pte.entry, machine().describe_context());

	return pte;
}

bool cammu_c4_device::get_access(const access_type mode, const u32 pte, const u32 ssw) const
{
	switch (mode)
	{
	case READ: return pte & 0x20;
	case WRITE: return pte & 0x10;
	case RMW: return (pte & 0x30) == 0x30;
	case EXECUTE: return pte & 0x08;
	}

	return false;
}

bool cammu_c3_device::get_access(const access_type mode, const u32 pte, const u32 ssw) const
{
	// FIXME: logic is not correct yet
	return true;

	const u8 column = (mode == EXECUTE ? i_cammu_column : d_cammu_column)[(ssw & SSW_PL) >> 9];
	const u8 access = cammu_matrix[column][(pte & PTE_PL) >> 3];

	switch (mode)
	{
	case READ: return access & R;
	case WRITE: return access & W;
	case RMW: return (access & (R | W)) == (R | W);
	case EXECUTE: return access & E;
	}

	return false;
}

// C100/C300 CAMMU protection level matrix
const u8 cammu_c3_device::i_cammu_column[] = { 1, 1, 0, 0, 1, 1, 0, 0, 3, 3, 2, 2, 3, 3, 2, 2 };
const u8 cammu_c3_device::d_cammu_column[] = { 1, 1, 0, 0, 3, 2, 3, 2, 3, 3, 2, 2, 3, 3, 2, 2 };

const cammu_c3_device::c3_access_t cammu_c3_device::cammu_matrix[][16] =
{
	{ RW,  RW,  RW,  RW,  RW,  RW,  RW,  RWE, RE,  R,   R,   R,   N,   N,   N,   N },
	{ N,   RW,  RW,  RW,  RW,  RW,  R,   RWE, N,   RE,  R,   R,   RE,  N,   N,   N },
	{ N,   N,   RW,  RW,  RW,  R,   R,   RWE, N,   N,   RE,  RE,  N,   RE,  N,   N },
	{ N,   N,   N,   RW,  R,   R,   R,   RWE, N,   N,   N,   RE,  RE,  N,   RE,  N }
};