【tiny4412】裸板MMU的开发

前言:MMU即Memory Managment Unit存储管理单元,其主要的作用有两个:一个是提供硬件机制的内存访问权限,另一个是管理虚拟存储器与物理存储器的控制信息以及负者将虚拟地址映射到物理地址

  • 平台:Ubuntu:16.04
  • 开发板:Tiny4412 1506
  • 依赖工具:arm-linux-gcc 4.5.1 dnw minicom (使用minitools传输在裸板开发时尝试过很多方法程序没有跑起来,后来干脆直接使用dnw的方式,有成功的小伙伴可以留言介绍下,感谢)

先看三星4412芯片手册给到的内存基地址:

在这里插入图片描述
tiny4412 1506 的内存为1G,其范围为0x40000000~0x8000000,因此我们程序的地址需要在该范围内;而虚拟地址映射到物理地址主要是通过构建映射表,映射时MMU则是通过先将CPU给的虚拟地址VA(virtual address)修改为MVA(modify virtual address),然后再在表中寻找对应的pa(physical address);
另外,CPU使用MMU的主要作用是作为一个虚拟存储器,这是为什么呢?很早之前计算机上运行的程序比较小,占用内存空间少,可以直接将所有程序的数据加载到内存中,但后来随着行业发展,各种各样的软件与操作系统推出越来越多的功能,单纯使用内存来保存运行时的程序与数据就显得捉襟见肘了,于是后来行业推出虚拟存储器的理念:例如有一台计算机,内存为2G,操作系统与应用运行时需要4G或者更多,那么此时内存空间满足不了它的需求,于是操作系统将当前运行的部分保存在内存中,未使用的部分保存在磁盘中,通过构建一个虚拟存储器,操作系统将需要用到哪部分程序加载到内存中,当内存不足时又将另一部分暂时用不到的程序退回到磁盘中,在协作过程中MMU就起到了关键作用;

这时候再来看tiny4412,它是一个32位的处理器,其寻址能力只有4G,其虚拟地址能够访问4G的范围,占用每个页表中的条目4个字节,由于页表中虚拟地址与物理地址是对应存在的,因此一个条目占用8个字节,那么映射表需要32G的空间,显然这样硬件成本不能支持;因此该问题要进行二级映射来
解决,32位地址的前12位作为基地址,后20位作为偏移量,即2的12次方为4K,将虚拟地址与物理地址的前12位对应,因此映射表只需要32K,即每个条目只需要记录基地址,然后CPU发出虚拟地址(mva)时通过偏移量计算确认对应的物理地址;
例如虚拟地址为0x00123456,基地址为001 偏移量为23456.先通过映射表找到对于的物理地址为0x70100000,然后加上偏移量就是实际的地址0x70123456。

0x001000000x70100000
0x002000000x70200000
0x003000000x70300000

此外,tiny4412的MMU在CP15的协处理器中,所以开启MMU时需要内嵌汇编去实现,用 mcr 和 mrc 指令将 arm 核心寄存器中的值的写到 cp15 寄存器中和从 cp15 寄存器中读到 arm 核心寄存器中(mrc与mcr在arm中主要用于从协处理器的寄存器与arm的寄存器写入与读出),另外大部分指令都需要在 PL1 以及更高的特权级下才能正常执行,这是因为 cp15 协处理器大多都涉及到系统和内存的设置,user 模式没有操作权限,user 模式仅能访问 cp15 中有限的几个寄存器比如:ISB、DSB、DMB、TPIDRURW、TPIDRURO 寄存器(这里有点扯远了,大家当作为了解)。这里以
mcr为例:

MRC <c> <coproc>, <opc1>, <Rt>, <CRn>, <CRm>{, <opc2>}
<c>:指令后缀,表示条件执行
<coproc>:协处理器的名称,cp0~cp15 分别对应名称 p0~p15
<opc1>:对于 cp15 而言,这一个参数填 0 即可。
<Rt>:arm 的通用寄存器
<CRn>:与 arm 核心寄存器交换数据的核心寄存器名,c0~c15
<CRm>:需要额外操作的协处理器的寄存器名,c0~c15,针对多种功能的 cp15 寄存器,需要使用 CRm 和 opc2 来确定 CRn 对应哪个寄存器实体。
<opc2>:可选,与 CRm搭配使用,同样是决定多功能寄存器中指定实体。

例如:

mrc     p15, 0, r0, c0, c1, 1

这条指令的含义是将 cp15 ID_PFR1 寄存器中的值读取到 arm 的 r0 寄存器中,其中 c0,c1,1 三个参数共同用来指定目标寄存器是 ID_PFR1

先创建ttb表:

void init_ttb(unsigned long *ttb_base)
{
   unsigned long va, pa; // 虚拟地址,物理地址

   memset(ttb_base, 0x00, 16 * 1024); // 创建一个16k的数组作为ttb映射表,每个段映射空间为1M
   for (va = 0x00000000; va < 0x10000000; va += 0x100000)
   {
      /* other */
      pa = va;
      ttb_base[va >> 20] = (pa & 0xfff00000) | 2;
   }
   for (va = 0x10000000; va < 0x14000000; va += 0x100000)
   {
      /* SFR */
      pa = va;
      ttb_base[va >> 20] = (pa & 0xfff00000) | 2;
   }
   for (va = 0x40000000; 0x80000000; va += 0x100000)
   {
      /* DRAM */
      pa = va;
      ttb_base[va >> 20] = (pa & 0xfff00000) | 2;
   }
}
void memset(char *buf, char ch, int size)
{
   int i;
   for (i = 0; i < size; i++)
   {
      /* code */
      buf[i] = ch;
   }
}
// 创建单条映射
void mmap(unsigned long *ttb_base, unsigned long va, unsigned long pa)
{
   ttb_base[va >> 20] = (pa & 0xfff00000) | 2;
}

必须使用用内嵌C的方式开启:

   __asm__ __volatile__(
       "mvn r0, #0 \n"
       "mcr p15, 0, r0, c3, c0, 0\n"

       "mcr p15, 0, %1, c2, c0, 0\n" // config ttb

       "mrc p15, 0, r0, c1, c0, 0\n"
       "orr %0, r0, %0\n"
       "mcr p15, 0, %0, c1, c0, 0\n" // open mmu
       :
       : "r"(c1_flags), "r"(ttb)
       : "r0");
   p = 0x12345678;
   printf("*p=0x%08x\n", *p);
}

编码完成后跟之前led点灯一样,先make进行编译成bin文件,然后使用三星给的dnw工具传到开发板上,再用go 70003000就行了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值