在学习S3C6410 MMU的地址映射的时候,竟然被地址加1的问题给卡住了。
void create_page_table(void)
{
unsigned long *ttb = (unsigned long *)0x50000000;
unsigned long vaddr, paddr;
vaddr = 0x50000000;
paddr = 0x50000000;
while (vaddr < 0x54000000)
{
*(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000);
vaddr += 0x100000;
paddr += 0x100000;
}
}
这里ttb是第一级表的首地址,为外部内存的首地址,S3C6410为0x50000000。然后建立一个映射表。
在while第一次循环的时候,vaddr>>20 为0x500,就相当于往0x50000500写入后面的数据,执行第二次的时候,vaddr>>20为0x501,就相当于往0x50000501写入后面的数据。等等,问题来了,arm对地址要求是4字节对齐的,怎么会有0x50000501这个地址了。
肯定是什么地方出现问题,但是代码是没有问题的,看来就是自己理解的问题。
最后,在别人的帮助下,才发现问题的所在。
原来地址加1和数据加1是不一样的。地址加1,要看是什么数据类型,不同的数据类型加的值不一样,对于int而言,地址加1就相当于地址加4。而数据加1就是单纯的数据加1。
这里ttb是unsigned long的指针,是占4个字节的。所以ttb在加1的时候,其实是会加4的,而加常数n的话,其实是会加n*4的。
循环第一次,vaddr >> 20的值为0x500,因为是和指针ttb相加,所以这个值要先乘以4在和ttb相加。0x500 * 4 = 0x1400。然后再和ttb相加,这个时候加的结果为0x50001400。这才是真正的地址,也是往这个地址写值的。循环第二次,vaddr >> 20的值0x501,因为是和指针ttb相加,所以这个值也先要先乘以4在和ttb相加。0x501 * 4 = 0x1404,在和ttb相加,这个时候加的结果为0x50001404。这个也才是真正的地址,也是往这个地址写值的。这样下来,写的地址才是4字节对齐的。
看来自己的C语言功底还不行啊。
用VS仿真了看看。
int main(void)
{
unsigned long *ttb = (unsigned long *)0x50000000;
unsigned long *address;
unsigned long vaddr = 0x50000000;
unsigned long paddr = 0x50000000;
while (vaddr < 0x54000000)
{
address = ttb + (vaddr >> 20);
vaddr += 0x100000;
paddr += 0x100000;
}
}
调试结果
循环第一次
看出ttb+(vaddr>>20)的值是0x50001400
循环第二次
看出ttb+(vaddr>>20)的值是0x50001404
循环第三次
看出ttb+(vaddr>>20)的值是0x50001404
以前,学C语言的时候,书上说地址加常数的话,要看地址的类型,然后再把常数乘以类型的字节数在和地址相加,也没有注意这个细节。这个在这里可就遇到问题了。
所以说,地址和常数相加的时候,要看地址的数据类型是什么,然后再将乘数乘以类型的字节数,最后在和地址相加。