Reputation: 13712
I want to enable data cache. I dont have much experience with ARM as I have mostly programmed for IA32. My understanding is that I need to enable MMU to enable data cache. As I dont need the virtual memory other wise so I want to enable MMU with one-to-one mapping between physical and virtual address space for all applications.
Any help/pointers/articles or code is appreciated.
Upvotes: 3
Views: 2833
Reputation: 315
For the sake of completeness, here's the additional code which is required to actually enable data caching once the MMU is set up (and also instruction caching, but this works also without MMU):
void enable_data_cache()
{
asm volatile( " mrc p15, 0, r0, c1, c0, 0" ); // Read c1 into r0
asm volatile( " orr r0, r0, #4" ); // Set bit 2: Dcache
asm volatile( " mcr p15, 0, r0, c1, c0, 0" ); // Return r0 to c1
}
void disable_data_cache()
{
asm volatile( "_disable_data_cache_start_:" );
asm volatile( " mrc p15, 0, r15, c7, c14, 3" ); // test, clean and invalidate
asm volatile( " bne _disable_data_cache_start_" );
asm volatile( " mov r0,#0" );
asm volatile( " mcr p15, 0, r0, c7, c5, 0" ); // invalidate I cache
asm volatile( " mcr p15, 0, r0, c7, c10, 4"); // drain write buffer
asm volatile( " mrc p15, 0, r0, c1, c0, 0" ); // Read c1 into r0
asm volatile( " bic r0, r0, #4" ); // Clear bit 2: disable Dcache
asm volatile( " mcr p15, 0, r0, c1, c0, 0" ); // Return r0 to c1
}
void clean_data_cache()
{
asm volatile( "_clean_data_cache_start_:" );
asm volatile( " mrc p15, 0, r15, c7, c14, 3" ); // test, clean and invalidate
asm volatile( " bne _clean_data_cache_start_" );
}
void enable_instruction_cache()
{
asm volatile( " mrc p15, 0, r0, c1, c0, 0" ); // Read c1 into r0
asm volatile( " orr r0, r0, #4096" ); // Set bit 12: Icache
asm volatile( " mcr p15, 0, r0, c1, c0, 0" ); // Return r0 to c1
}
void disable_instruction_cache()
{
asm volatile( " mrc p15, 0, r0, c1, c0, 0" ); // Read c1 into r0
asm volatile( " bic r0, r0, #4096" ); // Clearr bit 12: Icache
asm volatile( " mcr p15, 0, r0, c1, c0, 0" ); // Return r0 to c1
}
Upvotes: 0
Reputation: 71566
You are welcome to try something like this (see below). I wanted to do the same thing enable the data cache and make a one to one. Some tools and docs and code can over complicate the issue. You need the trm from arm for the specific mmu in your core and/or the datasheet for the core wherever the mmu is documented.
I am using an arm11 mpcore which is likely similar to what you have, the mmu is similar enough to the arm9 as well. the mpcore had some new big block table entry that was 16 times bigger than the next one but it was a total waste because you had to put 16 copies of the same entry in the mmu table.
You need a chunk of ram to hold the mmu table, you need to put an entry in the table for the physical address for that chunk of ram that is the first entry it may look up, look in the mmu table to find out about the mmu table kind of a thing. Likewise the interrupt vector table area (address zero) should be in there. use the coarsest entries you can.
Remember that control register space should not be cached, you should probably not enable the data cache until your mmu table is working, then start caching areas until your code crashes, then uncache that last thing.
Since you are aiming for a one to one mapping and not looking to use the mmu like an operating system would you can do what I did and build your mmu table ahead of time. An operating system would need routines for doing it in real time. The code below is building the table in assembler which is then linked in to the program. your solution may vary.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MMUTABLEBASE 0x00000000
#define MMUALLOC (0x8000>>2)
unsigned int mmu_table[MMUALLOC];
unsigned int mmu_table_owner[MMUALLOC];
#define TOP_LEVEL_WORDS (1<<((31-20)+1))
#define COARSE_TABLE_WORDS (1<<((19-12)+1))
#define SMALL_TABLE_WORDS (1<<((11-0)+1))
unsigned int base;
unsigned int nextfree;
unsigned int next_coarse_offset ( unsigned int x )
{
unsigned int mask;
mask=(~0)<<(10-2);
mask=~mask;
while(x&mask) x++; //lazy brute force
return(x);
}
unsigned int add_one ( unsigned int add, unsigned int flags )
{
unsigned int ra;
unsigned int rb;
unsigned int rc;
ra=add>>20;
if(mmu_table[ra])
{
printf("Address %08X already allocated\n",add);
return(1);
}
add=ra<<20;
rb=next_coarse_offset(nextfree);
rc=rb+COARSE_TABLE_WORDS;
if(rc>=MMUALLOC)
{
printf("Not enough room\n");
return(1);
}
nextfree=rc;
mmu_table[ra]=(MMUTABLEBASE+(rb<<2))|0x00000001;
for(ra=0;ra<COARSE_TABLE_WORDS;ra++)
{
mmu_table[rb+ra]=(add+(ra<<12))|0x00000032|flags;
mmu_table_owner[rb+ra]=(add+(ra<<12));
}
return(0);
}
int main ( void )
{
memset(mmu_table,0xF0,sizeof(mmu_table));
for(nextfree=0;nextfree<TOP_LEVEL_WORDS;nextfree++)
{
mmu_table[nextfree]=0x00000000;
mmu_table_owner[nextfree]=nextfree<<20;
}
if(add_one(0xD6000000,0x0000|8|4)) return(1);
if(add_one(0x00000000,0x0000|8|4)) return(1);
if(add_one(0xC3F00000,0x0000)) return(1);
if(add_one(0xCA000000,0x0000)) return(1);
printf(" .globl _start\n");
printf("_start:\n");
for(base=0;base<nextfree;base++)
{
printf(".word 0x%08X ;@ [0x%08X] 0x%08X\n",mmu_table[base],MMUTABLEBASE+(base<<2),mmu_table_owner[base]);
}
for(;base<MMUALLOC;base++)
{
printf(".word 0x00000000 ;@ [0x%08X]\n",MMUTABLEBASE+(base<<2));
}
printf(" b zreset\n");
for(base=0;base<15;base++) printf(" b zhang\n");
printf("zhang: b zhang\n");
printf("zreset:\n");
printf(" ldr pc,=reset\n");
printf("\n");
return(0);
}
Upvotes: 3
Reputation: 13712
Given below is the code to enable one-to-one mapping between physical and virtual address space:
#define NUM_PAGE_TABLE_ENTRIES 4096 /* 1 entry per 1MB, so this covers 4G address space */
#define CACHE_DISABLED 0x12
#define SDRAM_START 0x80000000
#define SDRAM_END 0x8fffffff
#define CACHE_WRITEBACK 0x1e
static inline void enable_mmu(void)
{
static U32 __attribute__((aligned(16384))) page_table[NUM_PAGE_TABLE_ENTRIES];
int i;
U32 reg;
/* Set up an identity-mapping for all 4GB, rw for everyone */
for (i = 0; i < NUM_PAGE_TABLE_ENTRIES; i++)
page_table[i] = i << 20 | (3 << 10) | CACHE_DISABLED;
/* Then, enable cacheable and bufferable for RAM only */
for (i = SDRAM_START >> 20; i < SDRAM_END >> 20; i++)
{
page_table[i] = i << 20 | (3 << 10) | CACHE_WRITEBACK;
}
/* Copy the page table address to cp15 */
asm volatile("mcr p15, 0, %0, c2, c0, 0"
: : "r" (page_table) : "memory");
/* Set the access control to all-supervisor */
asm volatile("mcr p15, 0, %0, c3, c0, 0" : : "r" (~0));
/* Enable the MMU */
asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (reg) : : "cc");
reg|=0x1
asm volatile("mcr p15, 0, %0, c1, c0, 0" : : "r" (reg) : "cc");
}
Upvotes: 3
Reputation: 6027
A 16K page directory containing a one-to-one virtual/physical mapping can be created with the following (untested) code:
/* Setup types for virtual addresses, physical address, and the page table. */
typedef unsigned long vaddr_t;
typedef unsigned long paddr_t;
typedef unsigned long pde_t;
/* Reserve space for a page directory. Must be 16k aligned. */
pde_t page_directory[1 << 12] ALIGNED(1 << 12);
/* Create a 1MB mapping in the given page directory from 'virt' to 'phys'. */
void set_large_page_mapping(pde_t *pd, vaddr_t virt, paddr_t phys)
{
pde_t entry = 0;
entry |= phys & 0xfff00000; /* Target of the mapping. */
entry |= 2; /* This is a 1MB section entry. */
entry |= 1 << 4; /* Enable caches (C). */
entry |= 1 << 3; /* Enable writeback (B). */
entry |= 3 << 10; /* Full read/write permission. */
pd[virt >> 20] = entry; /* Install the entry. */
}
/* Setup a page directory with one-to-one physical/virtual mappings. */
void setup_one_to_one_mappings(void)
{
unsigned long i;
/* Setup a mapping for each 1MB region in the virtual address space. */
for (i = 0; i < (1 << 12); i++) {
/* Map the virtual address "i << 20" to phys address "i << 20". */
set_large_page_mapping(page_directory, i << 20, i << 20);
}
/* TODO: Write function to install this page directory and enable the MMU. */
enable_mmu(page_directory);
}
The enable_mmu
function still needs to be written, which will need to:
Each of these instructions tend to be CPU-specific, but examples should (hopefully) be available for your hardware elsewhere (or, failing that, by looking at sources to other operating systems, such as Linux or FreeBSD). Additionally, for testing, you probably need only worry about the last two points to get started.
Upvotes: 3