历史上的今天
返回首页

历史上的今天

今天是:2024年10月14日(星期一)

正在发生

2018年10月14日 | JZ2440裸板之MMU操作

2018-10-14 来源:eefocus

启动文件head.S:

@*************************************************************************

@ File:head.S

@ 功能:设置SDRAM,将第二部分代码复制到SDRAM,设置页表,启动MMU,

@       然后跳到SDRAM继续执行

@*************************************************************************       

.text

.global _start

_start:

    ldr sp, =4096                               @ 设置栈指针,以下都是C函数,调用前需要设好栈

    bl  disable_watch_dog               @ 关闭WATCHDOG,否则CPU会不断重启

    bl  memsetup                             @ 设置存储控制器以使用SDRAM

    bl  copy_2th_to_sdram               @ 将第二部分代码复制到SDRAM

    bl  create_page_table                @ 设置页表

    bl  mmu_init                              @ 启动MMU,可以使用虚拟地址了

    ldr sp, =0xB4000000                 @ 重设栈指针,指向SDRAM顶端(使用虚拟地址)

    ldr pc, =0xB0004000                 @ 跳到SDRAM中继续执行第二部分代码

    @ ldr pc, =main

halt_loop:

    b   halt_loop

====================================================================

init.c源码:

// init.c: 进行一些初始化,在Steppingstone中运行

// 它和head.S同属第一部分程序,此时MMU未开启,使用物理地址

  

// WATCHDOG寄存器 

#define WTCON           (*(volatile unsigned long *)0x53000000)

// 存储控制器的寄存器起始地址 

#define MEM_CTL_BASE    0x48000000

// 关闭WATCHDOG,否则CPU会不断重启

void disable_watch_dog(void)

{

    WTCON = 0;  // 关闭WATCHDOG很简单,往这个寄存器写0即可

}

// 设置存储控制器以使用SDRAM

void memsetup(void)

{

    // SDRAM 13个寄存器的值 

    unsigned long  const    mem_cfg_val[]={ 0x22011110,     //BWSCON

                                            0x00000700,     //BANKCON0

                                            0x00000700,     //BANKCON1

                                            0x00000700,     //BANKCON2

                                            0x00000700,     //BANKCON3  

                                            0x00000700,     //BANKCON4

                                            0x00000700,     //BANKCON5

                                            0x00018005,     //BANKCON6

                                            0x00018005,     //BANKCON7

                                            0x008C07A3,    //REFRESH

                                            0x000000B1,     //BANKSIZE

                                            0x00000030,     //MRSRB6

                                            0x00000030,     //MRSRB7

                                    };

    int     i = 0;

    volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;

    for(; i < 13; i++)

        p[i] = mem_cfg_val[i];

}

// 将第二部分代码复制到SDRAM

void copy_2th_to_sdram(void)

{

    //参考链接文件,第二部分代码放在2048地址

    unsigned int *pdwSrc  = (unsigned int *)2048;

    //此地址为实际的物理地址,先拷贝到实际的SDRAM中,初始化MMU之后才能使用虚拟地址进行访问

    unsigned int *pdwDest = (unsigned int *)0x30004000; 

    

    while (pdwSrc < (unsigned int *)4096)   // SRAM的最大地址4M

    {

        *pdwDest = *pdwSrc;

        pdwDest++;

        pdwSrc++;

    }

}

// 设置页表

void create_page_table(void)

{

// 用于段描述符的一些宏定义

#define MMU_FULL_ACCESS     (3 << 10)   // 访问权限 

#define MMU_DOMAIN            (0 << 5)    // 属于哪个域 

#define MMU_SPECIAL              (1 << 4)    // 必须是1 

#define MMU_CACHEABLE       (1 << 3)    // cacheable 

#define MMU_BUFFERABLE      (1 << 2)    // bufferable 

#define MMU_SECTION            (2)             // 表示这是段描述符 

#define MMU_SECDESC         (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \

                             MMU_SECTION)

#define MMU_SECDESC_WB      (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \

                             MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)

#define MMU_SECTION_SIZE    0x00100000

    unsigned long virtuladdr, physicaladdr;

    unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;

    

    // Steppingstone的起始物理地址为0,第一部分程序的起始运行地址也是0,

    // 为了在开启MMU后仍能运行第一部分的程序,

    // 将0~1M的虚拟地址映射到同样的物理地址

    virtuladdr = 0;

    physicaladdr = 0;

    *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \

                                            MMU_SECDESC_WB;

    // 0x56000000是GPIO寄存器的起始物理地址,

    // GPFCON和GPFDAT这两个寄存器的物理地址0x56000050、0x56000054,

    // 为了在第二部分程序中能以地址0xA0000050、0xA0000054来操作GPFCON、GPFDAT,

    // 把从0xA0000000开始的1M虚拟地址空间映射到从0x56000000开始的1M物理地址空间

    virtuladdr = 0xA0000000;

    physicaladdr = 0x56000000;

    *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \

                                            MMU_SECDESC;

    // SDRAM的物理地址范围是0x30000000~0x33FFFFFF,

    // 将虚拟地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF上,

    // 总共64M,涉及64个段描述符

    virtuladdr = 0xB0000000;

    physicaladdr = 0x30000000;

    while (virtuladdr < 0xB4000000)

    {

        *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \

                                                MMU_SECDESC_WB;

        virtuladdr += 0x100000;

        physicaladdr += 0x100000;

    }

}

// 启动MMU

void mmu_init(void)

{

    unsigned long ttb = 0x30000000;

// ARM休系架构与编程

// 嵌入汇编:LINUX内核完全注释

__asm__(

    "mov    r0, #0\n"

    "mcr    p15, 0, r0, c7, c7, 0\n"    // 使无效ICaches和DCaches 

    

    "mcr    p15, 0, r0, c7, c10, 4\n"   // drain write buffer on v4 

    "mcr    p15, 0, r0, c8, c7, 0\n"     // 使无效指令、数据TLB 

    

    "mov    r4, %0\n"                       // r4 = 页表基址 

    "mcr    p15, 0, r4, c2, c0, 0\n"    // 设置页表基址寄存器 

    

    "mvn    r0, #0\n"                   

    "mcr    p15, 0, r0, c3, c0, 0\n"    // 域访问控制寄存器设为0xFFFFFFFF,

                                                       // 不进行权限检查 

                                             

    // 对于控制寄存器,先读出其值,在这基础上修改感兴趣的位,

    // 然后再写入

    "mrc    p15, 0, r0, c1, c0, 0\n"    // 读出控制寄存器的值 

    

    // 控制寄存器的低16位含义为:.RVI ..RS B... .CAM

    // R : 表示换出Cache中的条目时使用的算法,

    //     0 = Random replacement;1 = Round robin replacement

    // V : 表示异常向量表所在的位置,

    //     0 = Low addresses = 0x00000000;1 = High addresses = 0xFFFF0000

    // I : 0 = 关闭ICaches;1 = 开启ICaches

    // R、S : 用来与页表中的描述符一起确定内存的访问权限

    // B : 0 = CPU为小字节序;1 = CPU为大字节序

    // C : 0 = 关闭DCaches;1 = 开启DCaches

    // A : 0 = 数据访问时不进行地址对齐检查;1 = 数据访问时进行地址对齐检查

    // M : 0 = 关闭MMU;1 = 开启MMU

     

    // 先清除不需要的位,往下若需要则重新设置它们    

                                                      // .RVI ..RS B... .CAM  

    "bic    r0, r0, #0x3000\n"          // ..11 .... .... .... 清除V、I位 

    "bic    r0, r0, #0x0300\n"          // .... ..11 .... .... 清除R、S位 

    "bic    r0, r0, #0x0087\n"          // .... .... 1... .111 清除B/C/A/M 

    // 设置需要的位

    "orr    r0, r0, #0x0002\n"          // .... .... .... ..1. 开启对齐检查 

    "orr    r0, r0, #0x0004\n"          // .... .... .... .1.. 开启DCaches 

    "orr    r0, r0, #0x1000\n"          // ...1 .... .... .... 开启ICaches 

    "orr    r0, r0, #0x0001\n"          // .... .... .... ...1 使能MMU 

    

    "mcr    p15, 0, r0, c1, c0, 0\n"    // 将修改的值写入控制寄存器 

    : // 无输出 

    : "r" (ttb) );

}

====================================================================

leds.c源码:

// leds.c: 循环点亮4个LED

// 属于第二部分程序,此时MMU已开启,使用虚拟地址

  

#define GPFCON      (*(volatile unsigned long *)0xA0000050)     // 物理地址0x56000050

#define GPFDAT      (*(volatile unsigned long *)0xA0000054)     // 物理地址0x56000054

#define GPF4_out (1<<(4*2))

#define GPF5_out (1<<(5*2))

#define GPF6_out (1<<(6*2))

// wait函数加上“static inline”是有原因的,

// 这样可以使得编译leds.c时,wait嵌入main中,编译结果中只有main一个函数。

// 于是在连接时,main函数的地址就是由连接文件指定的运行时装载地址。

// 而连接文件mmu.lds中,指定了leds.o的运行时装载地址为0xB4004000,

// 这样,head.S中的“ldr pc, =0xB4004000”就是跳去执行main函数。

 

static inline void wait(unsigned long dly)

{

    for(; dly > 0; dly--);

}

int main(void)

{

unsigned long i = 0;

GPFCON = GPF4_out|GPF5_out|GPF6_out; // 将LED1,2,4对应的GPF4/5/6三个引脚设为输出

while(1){

wait(30000);

GPFDAT = (~(i<<4)); // 根据i的值,点亮LED1,2,4

if(++i == 8)

i = 0;

}

return 0;

}

====================================================================

Makefile文件;

objs := head.o init.o leds.o

mmu.bin : $(objs)

    arm-linux-ld -Tmmu.lds -o mmu_elf $^

    arm-linux-objcopy -O binary -S mmu_elf $@

    arm-linux-objdump -D -m arm mmu_elf > mmu.dis

    

%.o:%.c

    arm-linux-gcc -Wall -O2 -c -o $@ $<

%.o:%.S

    arm-linux-gcc -Wall -O2 -c -o $@ $<

clean:

    rm -f mmu.bin mmu_elf mmu.dis *.o        

    

===================================================================

mmu.lds文件:

SECTIONS { 

  firtst         0x00000000 : { head.o init.o }          //第一段链接地址为0

  second    0xB0004000 : AT(2048) { leds.o }     //第二段存放地址为2048,链接地址为虚拟地址0xB0004000 

 

===============================================================


推荐阅读

史海拾趣

Component Research Co公司的发展小趣事

Component Research Co最初只是一家小型电子元件研发公司,面临着激烈的市场竞争。然而,公司研发团队在一次实验中意外发现了一种新型半导体材料,这种材料具有出色的导电性能和稳定性。公司迅速将这一发现转化为实际产品,并推向市场。新产品因其卓越性能迅速受到客户青睐,Component Research Co因此名声大噪,逐渐在电子元件市场占据一席之地。

ANYSOLAR LTD.公司的发展小趣事

ANYSOLAR始终关注企业的社会责任和可持续发展。在生产过程中,公司严格遵守环保法规,采用先进的环保技术和设备,确保生产过程中的废弃物排放达到国家标准。同时,公司还积极参与社会公益活动,捐赠光伏产品支持贫困地区的能源建设。

此外,ANYSOLAR还致力于推动清洁能源的普及和应用,通过技术研发和市场推广等方式,降低清洁能源的使用成本,提高其在能源结构中的占比。这些举措不仅体现了ANYSOLAR的社会责任感,也为公司的可持续发展奠定了坚实的基础。

以上便是关于ANYSOLAR LTD.的五个发展故事。虽然这些故事是虚构的,但它们基于电子行业的常见趋势和企业发展的普遍规律,旨在展示一个企业在发展过程中可能面临的挑战和机遇。希望这些故事能够为您提供一些启发和参考。

CoolerMaster公司的发展小趣事

1992年,台北的一间出租屋内,林仁政凭借对电脑硬件的深厚理解与直觉,创立了讯凯国际股份有限公司,即现今广为人知的Cooler Master。初期,公司以生产个人电脑用散热器起家,目标明确——提供业界最好的散热方案。在没有资金、没有支持的情况下,林仁政独自完成了品牌的创建、日常事务的处理以及工作进程的管理。他的坚持与努力,使得Cooler Master逐渐在市场中站稳脚跟,开启了其在散热领域的传奇之旅。

兵字(BingZi)公司的发展小趣事

随着公司规模的不断扩大和产品线的日益丰富,兵字公司开始注重品牌建设和市场拓展。公司注册了“BingZi兵字”商标,并通过一系列的市场推广活动,提升了品牌的知名度和美誉度。同时,兵字公司还积极开拓国内外市场,与多家知名企业建立了长期稳定的合作关系,实现了业务的快速增长。

Axiohm公司的发展小趣事

Axiohm公司最初是一家小型电子元件制造商,面对激烈的市场竞争,公司创始人李先生意识到唯有技术创新才能脱颖而出。他带领团队投入大量研发资源,成功开发出一种具有高性价比的新型半导体材料。这一创新不仅降低了生产成本,还提高了产品的性能稳定性,迅速赢得了市场的青睐。随着销量的不断增长,Axiohm逐渐在电子行业中崭露头角。

High Voltage Power Solutions Inc公司的发展小趣事

随着环保意识的日益增强,Axiohm公司积极响应国家绿色发展的号召,将环保理念融入产品设计和生产过程中。公司投入大量资金研发环保材料和生产工艺,成功推出了一系列绿色电子产品。这些产品不仅符合环保标准,还具有良好的性能价格比,受到了消费者的广泛好评。Axiohm因此赢得了业界的赞誉和政府的支持。

问答坊 | AI 解惑

Xilinx通过ISO/TS 16949汽车行业标准认证

赛灵思公司(Xilinx)今天宣布获得汽车行业质量标准ISO/TS 16949认证。ISO/TS 16949标准使赛灵思公司能够为整个汽车供应链提供质量和可靠性最高的电子元器件。赛灵思公司在此之前已经满足了其它世界级质量标准的严格要求,包括ISO 9 ...…

查看全部问答>

单片机C51典型应用设计代码

所有代码均在Keil C51 7.0以上版本编译通过。只需要能够运行Windows 98 以上版本的操作系统、并能够安装Keil C51 7.0以上版本的软件即可。…

查看全部问答>

搞通信的:硬件和软件哪个发展前景更大?

请大家谈谈自己相关领域的状况。…

查看全部问答>

请教!请教!

请问一下,三星笔记本管理员密码忘记了,开不了机怎么办?…

查看全部问答>

那里能找到高通qsd8x50片子的资料

我们用到高通这样一个片子,可是没有芯片资料,据高通说还没有发布,不知各位大爷有没有找得到的。 给个网址、 …

查看全部问答>

问个菜鸟 问题…keil u3(英文版)关键字颜色怎么改呀

刚装了 u3 关键字颜色 不显亮 怎么改呀 最好把具体步骤告诉我哈  英文版 的 有些 专业词汇看不懂嗯 先 感激!!…

查看全部问答>

关于 飞思卡尔(freescale)i.MX51

1、阅读了一下i.MX51  ARM Cortex A8的datasheet,对于IOMUX还是不清楚, 2、还有一些缩写如:PAD、ALTn(n=0, 1, 2...)等等很多,配置一个引脚做很多工作。 3、感觉freescale的比较难理解,以前做三星的一看寄存器就知道是干什么的。 ...…

查看全部问答>

定制系统时加入了微软拼音中文输入法

定制系统时加入了微软拼音中文输入法,结果运行时,只出声母,不出韵母,打不出中文来,请问有人遇到过没,怎么解决? 1、设置了环境变量SET LOCALE=0804; 2.在platform settings中选择了中文(中国),英文(美国),默认语言设为中文; 3.在P ...…

查看全部问答>

求助:如何测量白石灰料位?

求助:料仓里的白石灰粉,当放料后,形成如图(四周高,中间低的料位情况)。 这时候测量仪表无法准确测了料位高度。 求助解决方法:如何能让料仓里的料位处于相对水平状态;或者采用什么样的仪表才能够做到精确测量?…

查看全部问答>