u-boot移植(八)---代码修改---存储控制器--MMU

发布时间:2023-07-03  

一、MMU介绍

1.1 虚拟地址与物理地址

  建立两个应用程序,hello1.c和hello2.c,然后运行:

  hello1.c

  

  hello2.c

  

  运行结果如下:

  

   可以看到两个结果打印的地址是一样的,都为 0x601040,这说明两段程序都运行于同一个地址中。我们的死循环程序又保证了两个程序在同时运行。

   

  对于2440来说,如下图:

  

  CPU只管发出地址,读写数据。不管地址是否是物理地址还是虚拟地址。

  写程序的时候,链接地址也没有物理地址和虚拟地址。链接地址是CPU看到的,是从CPU的角度来说的。

1.2 虚拟地址转换成物理地址

  对与ARM来说,虚拟地址(VA)转换成物理地址(PA)是通过表格来转换的。这个表格就是页表。

  建立映射的简单步骤是:

  • 建立表格:虚拟地址到物理地址的映射

  • 表格地址告诉给MMU:表格在内存中,将表格的首地址告诉MMU

  • 启动MMU  

二、代码

2.1 start.S (archarmcpuarm920t) 

  start.S中的cpu_init_crit进行了MMU的使能设置,u-boot启动的时候的是不使能MMU的。MMU的使能涉及到CP15协处理器。

  代码段如下:

 1     /*

 2      * disable MMU stuff and caches

 3      */

 4     mrc    p15, 0, r0, c1, c0, 0        /* 将 CP15 的寄存器 C1 的值读到 r0 中 */

 5     /* C1寄存器的8,9,13位被清除

 6      * S(bit[8])在基于 MMU 的存储系统中,本位用作系统保护

 7      * R(bit[9])在基于 MMU 的存储系统中,本位用作 ROM 保护

 8      * V(bit[13])

 9      *        对于支持高端异常向量表的系统,本控制位控制向量表的位置

10      *        0 :选择低端异常中断向量 0x0~0x1c 

11      *        1 :选择高端异常中断向量0xffff0000~ 0xffff001c

12      *        对于不支持高端异常向量表的系统,读取时该位返回0,写入时忽略

13      */

14     bic    r0, r0, #0x00002300    @ clear bits 13, 9:8 (--V- --RS)

15     /*     C1寄存器的0,1,2,7位被清除

16      *    M(bit[0])

17      *                0 :禁止 MMU 或者 PU 

18      *                1 :使能 MMU 或者 PU

19      *                如果系统中没有MMU及PU,读取时该位返回0,写入时忽略该位

20      *  A(bit[1])

21      *                0 :禁止地址对齐检查

22      *                1 :使能地址对齐检查

23      *  C(bit[2])

24      *                当数据cache和指令cache分开时,本控制位禁止/使能数据cache。

25      *                当数据cache和指令cache统一时,该控制位禁止/使能整个cache。

26      *                0 :禁止数据 / 整个 cache 

27      *                1 :使能数据 / 整个 cache

28      *                如果系统中不含cache,读取时该位返回0.写入时忽略

29      *                当系统中不能禁止cache 时,读取时返回1.写入时忽略

30      *  B(bit[7])

31      *                对于存储系统同时支持big-endian和little-endian的ARM系统,本控制位配置系统的存储模式

32      *                0 : little endian  

33      *                1 : big endian

34      *                对于只支持little-endian的系统,读取时该位返回0,写入时忽略

35      *                对于只支持big-endian的系统,读取时该位返回1,写入时忽略

36      */

37     bic    r0, r0, #0x00000087    @ clear bits 7, 2:0 (B--- -CAM)

38     /* C1寄存器的bit1置1,即使能地址对齐 */

39     orr    r0, r0, #0x00000002    @ set bit 1 (A) Align

40     /* C1寄存器的bit12置1,即使能指令 cache */

41     /* I(bit[12])

42      *                当数据cache和指令cache是分开的,本控制位禁止/使能指令cache

43      *                0 :禁止指令 cache  

44      *                1 :使能指令 cache

45      *                    如果系统中使用统一的指令cache和数据cache或者系统中不含cache,读取该位时返回0,写入时忽略。

46      *                    当系统中的指令cache不能禁止时,读取时该位返回1,写入时忽略

47      */

48     orr    r0, r0, #0x00001000    @ set bit 12 (I) I-Cache

49     mcr    p15, 0, r0, c1, c0, 0        /* 将 r0 的值写到 CP15 的寄存器 C1 中 */

50 

51     /*

52      * before relocating, we have to setup RAM timing

53      * because memory timing is board-dependend, you will

54      * find a lowlevel_init.S in your board directory.

55      */

56     mov    ip, lr                    /* 保存函数地址,用于返回 */

  之后start.S 中的代码会进入 _main(crt0.S (archarmlib))中运行。


2.2 crt0.S (archarmlib)

  ctr0.S中运行 _main 函数,再此函数中,运行到 board_init_f(单板启动前初始化),然后执行到链表 init_sequence_f,在其中对MMU进行初始化 reserve_mmu。


2.2.1 reserve_mmu


 1 static int reserve_mmu(void)

 2 {

 3     /* reserve TLB table */

 4     gd->arch.tlb_size = PGTABLE_SIZE;    //PGTABLE_SIZE = 4096 *4 预留16kb的MMU页表

 5     gd->relocaddr -= gd->arch.tlb_size;//gd->relocaddr = gd->relocaddr - 16K = 0x33ffc000

 6 

 7     /* round down to next 64 kB limit */

 8     gd->relocaddr &= ~(0x10000 - 1);//64kb对齐    gd->relocaddr = 0x33ff0000

 9 

10     gd->arch.tlb_addr = gd->relocaddr;//gd->arch.tlb_addr = 0x33ff0000

11     debug("TLB table from %08lx to %08lxn", gd->arch.tlb_addr,

12           gd->arch.tlb_addr + gd->arch.tlb_size);

13     return 0;

14 }

  PGTABLE_SIZE的大小我们可以看 u-boot.dis中得到:

  

  第一行就是 PGTABLE_SIZE 得大小。

  在执行完链表以后,跳回_main中继续执行,然后执行到board_init_r(Board_r.c (common)板启动后初始化代码)。

  在board_init_r同样会执行一个链表,init_sequence_r。在链表中执行各种初始化函数,然后在执行到 board_init 中,在board_init中,进行icache_enable和dcache_enable。

  这两个函数都会调用 cache_enable 函数。在其中就有MMU的设置。

2.2.2 cache_enable 

  cache_enable

 1 static void cache_enable(uint32_t cache_bit)

 2 {

 3     uint32_t reg;

 4 

 5     /* The data cache is not active unless the mmu is enabled too */

 6     if ((cache_bit == CR_C) && !mmu_enabled())

 7         mmu_setup();

 8     reg = get_cr();    /* get control reg. */

 9     cp_delay();

10     set_cr(reg | cache_bit);

11 }


  现在来看mmu_setup 函数


 1 /* to activate the MMU we need to set up virtual memory: use 1M areas */

 2 static inline void mmu_setup(void)

 3 {

 4     int i;

 5     u32 reg;

 6 

 7     arm_init_before_mmu(); //空函数

 8     /* Set up an identity-mapping for all 4GB, rw for everyone */

 9     for (i = 0; i < 4096; i++)

10         set_section_dcache(i, DCACHE_OFF); //传入DCACHE_OFF参数,建立页表地址

11 

12     for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {

13         dram_bank_mmu_setup(i);    //设置DRAM的页表大小和地址

14     }

15 

16     /* Set the access control to all-supervisor */

17     asm volatile("mcr p15, 0, %0, c3, c0, 0"

18              : : "r" (~0));    //MMU进行初始化

19 

20     arm_init_domains();    //空函数

21 

22     /* and enable the mmu */

23     reg = get_cr();    /* get control reg. */

24     cp_delay();

25     set_cr(reg | CR_M);

26 }

27 

28 arch/arm/include/asm/system.h

29 /* Size of an MMU section */

30 enum {

31     MMU_SECTION_SHIFT    = 20,

32     MMU_SECTION_SIZE    = 1 << MMU_SECTION_SHIFT,

33 };

34 

35 /* 页表建立函数,每一页为1M,页的起始地址为section */

36 void set_section_dcache(int section, enum dcache_option option)

37 {

38     u32 *page_table = (u32 *)gd->arch.tlb_addr; //page_table取出来,16K

39     u32 value;

40 

41     value = (section << MMU_SECTION_SHIFT) | (3 << 10); //2的20次方为1M

42     value |= option;

43     page_table[section] = value;

44 }

45 

46 __weak void dram_bank_mmu_setup(int bank)

47 {

48     bd_t *bd = gd->bd;

49     int    i;

50 

51     debug("%s: bank: %dn", __func__, bank);

52     for (i = bd->bi_dram[bank].start >> 20;

53          i < (bd->bi_dram[bank].start >> 20) + (bd->bi_dram[bank].size >> 20);

54          i++) {

55 

56         set_section_dcache(i, DCACHE_WRITETHROUGH);

57     }

58 }


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>