jz2440内存实验(八)

前面花了大量力气去看芯片手册和SDRAM相关的管脚定义相关的内容。

下面就应该把这个内存芯片给用起来,跑一段程序看看。

在(四)中提到,机器初始化需要做的工作:

1.关闭看门狗

2.初始化时钟

3.初始化内存

4.为C语言初始化堆栈。

这里因为位对汇编语言的熟悉程序不够,且不说不是不够,是根本不想花时间研究指令怎么用。

还有更多的是对于debug汇编语言的恐慌,所以原则是尽量少用汇编。

废话不多说了,那么怎么去实验呢?

1.为C语言初始化堆栈

2.关看门狗

3.初始化时钟(忽略)

4.初始化SDRAM

5.拷贝有关代码到内存

6.跳转到内存中执行代码

head.S

.global _start
_start:
    ldr sp, =4096                            @ 设置堆栈指针
    bl  disable_watch_dog               @ 关闭WATCHDOG,否则CPU会不断重启
    bl  memsetup                             @ 设置存储控制器
    bl  copy_code_to_sdram              @ 复制代码到SDRAM中

    ldr sp, =0x34000000                 @ 重设栈指针,指向SDRAM顶端
    ldr pc, =0x30000000                 @ 跳到SDRAM中继续执行
                                                      @ 这里如何寻找main函数呢?这里提一个问题!

halt_loop:
    b   halt_loop

disable_watch_dog

memsetup

copy_code_to_sdram

这些代码都放到C语言实现了。

SP PC分别代表堆栈和CPU运行代码的位置,我们全部都转移到SDRAM上

下面看一下代码的实现:

init.c

unsigned long  addr_sdram_stack = 0x34000000;
unsigned long  addr_sdram_base = 0x30000000;
/* WATCHDOG寄存器 */
#define P_WTCON           ((volatile unsigned long *)0x53000000)
/* 存储控制器的寄存器起始地址 */
#define MEM_CTL_BASE    0x48000000
/*
 * 关闭WATCHDOG,否则CPU会不断重启
 */
void disable_watch_dog(void)
{
//      ClearBit(*P_WTCON,5);
//        setNbit(*P_WTCON,1,5,0x0);
      *P_WTCON = 0;
}

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];
}

void copy_code_to_sdram(void)
{
    unsigned int *pdwSrc  = (unsigned int *)2048;
    unsigned int *pdwDest = (unsigned int *)addr_sdram_base;

    while (pdwSrc < (unsigned int *)4096) //2k-4k复制
    {
        *pdwDest = *pdwSrc;
        pdwDest++;
        pdwSrc++;
    }
}

这样我们跳入到sdram的代码就是这些了,

然后我们用之前写过的非常老土的一个小程序验证一下。

leds.c

#define GPBCON      (*(volatile unsigned long *)0x56000050)
#define GPBDAT      (*(volatile unsigned long *)0x56000054)

int main()
{
    GPBCON = 0x00000100;    // 设置GPB4为输出口, 位[8:7]=0b01
    GPBDAT = 0x00000000;    // GPB4输出0,LED1点亮

    return 0;
}

Makefile

objs := head.o init.o leds.o
memory.bin : $(objs)
        arm-linux-ld -Tmem.lds -o mem.elf $^
        arm-linux-objcopy -O binary -S mem.elf [email protected]
        arm-linux-objdump -D -m arm mem.elf > mem.dis

#[email protected]目标文件,$^--所有的依赖文件,$<--第一个依赖文件
%.o:%.c
        arm-linux-gcc -Wall -O2 -c -o [email protected] $<
%.o:%.S
        arm-linux-gcc -Wall -O2 -c -o [email protected] $<
clean:
        rm -f *.dis *.bin *.elf *.o ../../pub/*.o

这里我们将链接地址改了一下,前面也看到过,我们嫌弃复制一大堆运行的过的代码很LOW,我们不复制初始化的代码。

为了达成这个效果,我们将代码分开存放。

从Makefile arm-linux-ld -Tmem.lds -o mem.elf $^

SECTIONS {

firtst    0x00000000 : { head.o init.o ../../pub/bit.o}

second    0x30000000 : AT(2048) { leds.o }

}

我们将程序分开存放 初始化代码放到0地址

我们应用程序放到  0x30000000 也就是sdram的开始地址

实验看看效果,果然灯亮了。

我们在head.S中只是随意的设置了,入口地址。我们反汇编看一下main函数的地址。

这个我们看到确实是3000 0000,要是我们加一些函数呢?

我们随便加了一个空函数,我们发现地址变了。

我们测试一下修改head.S 设置pc的地址为0x3000 0004,应该不成问题!

那么我们能不要人工计算这个,很麻烦。也容易弄错。

bl main  发现这样出错了!

: relocation truncated to fit: R_ARM_PC24 main

bl、b等指令 跳转范围在32MB,显然就算可以调整过去这样的代码还是有很大的风险。

这里我想到一个取巧的办法:

我的main函数的.C文件不会有其他函数出现。

比如我引用的一个其他函数.abc 我们放到另外的文件里,然后修改一些链接规则

SECTIONS {
  firtst    0x00000000 : { head.o init.o ../../pub/bit.o }
  second    0x30000000 : AT(2048) { leds.o }
  third     :{1.o}
}

记得在MakeFile编译一下新加的文件哦。

PS:想找一个办法自动定位出main的链接地址,那么写程序就方便很多,不需要偷偷摸摸的另外在单独函数中弄,目前还没看到好的办法,暂时先这样吧。

后面才发现使用 ldr pc,=main 就好了,之前可能Makefile没有写好,报错了,以为不行。

时间: 2024-10-13 09:53:12

jz2440内存实验(八)的相关文章

实验八 进程的切换和系统的一般执行过程

实验八 进程的切换和系统的一般执行过程 20135114王朝宪 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.进程切换的关键代码switch_to分析 1.1 进程调度与进程调度的时机分析 操作系统原理中介绍了大量进程调度算法,这些算法从实现的角度看仅仅是从运行队列中选择一个新进程,选择的过程中运用了不同的策略而已. 对于理解操作系统的工作机制,反而是进程的调度时机与进程的切换机制

Linux基础入门(新版)(实验五至实验八)

实验五 环境变量与文件查找 (环境变量的作用与用法,及几种搜索文件的方法) 一.环境变量 1.变量 (1)常变量与值是一对一的关系 (2)变量的作用域即变量的有效范围(比如一个函数中.一个源文件中或者全局范围),在该范围内只能有一个同名变量.一旦离开则该变量无效,如同不存在这个变量一般. 这里是一个例子: declare命令创建一个变量名为 tmp 的变量: $ declare tmp 使用=号赋值运算符为变量 tmp 赋值为 shiyanlou:$ tmp=shiyanlou 读取变量的值,使

实验八——函数定义及调用总结

实验八--函数定义及调用总结 1.本次课学习到的知识点: (1)void为不反回结果的函数,且void不能省略,否则默认为int,函数体中没有表达式的return语句,也可省略return. (2)不返回结果的函数在定义.调用.参数传递.函数声明上,思路与以前相同,适用于把一些确定的.相对独立的程序功能封装成函数. (3)局部变量:定义在函数的内部,且有效作用范局部变量一般定义在函数或复合语句的开始处,围局限于所在的函数内部,形参是局部变量. (4)不能定义在中间位置. (5)全局变量:定义在函

实验八:RIPv1

实验八:RIPv1 在GNS3上连接设备 配置路由器 (1)R1 (2)R2 (3)R3 (4)R4

Packet Tracer 5.0实验(八) 路由器静态路由配置

Packet Tracer 5.0实验(八) 路由器静态路由配置 一.实验目标 掌握静态路由的配置方法和技巧: 掌握通过静态路由方式实现网络的连通性: 熟悉广域网线缆的连接方式: 二.实验背景 学校有新旧两个校区,每个校区是一个独立的局域网,为了使新旧校区能够正常相互通讯,共享资源,每个校区出口利用一台路由器进行连接,两台路由器间学校申请了一条2M的DDN专线进行相连,要求你做适当配置实现两个校区间的正常相互访问. 三.技术原理 路由器属于网络层设备,能够根据IP包头的信息,选择一条最佳路径将数

VxWorks实验八 信号

实验八信号1 实验目的1.学习使用信号2 实验内容在实验一建立的 project 中,编写一段信号处理程序,将其与SIGINT 相关连,使用kill()发送SIGINT 信号并调用信号处理程序.观察运行结果.3 实验设备及工具1.硬件:a) PC 机2.软件:a) PC 机操作系统Windows2000 或windowsXPb) Tornado2.24 实验原理 信号可以用来通知任务处理特定的事件.当引起一个信号的事件发生时,信号产生(generated).当处理事件的任务激活时,信号释放(de

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验八:PS/2模块② — 键盘与组合键

实验八:PS/2模块② — 键盘与组合键 实验七之际,我们学习如何读取PS/2键盘发送过来的通码与断码,不过实验内容也是一键按下然后释放,简单按键行为而已.然而,实验八的实验内容却是学习组合键的按键行为. 不知读者是否有类似的经历?当我们使用键盘的时候,如果5~6按键同时按下,电脑随之便会发出“哔哔”的警报声,键盘立即失效.这是键盘限制设计,不同产品也有不同限制的按键数量.默认下,最大按键数量是5~7个.所谓组合键就是两个以上的按键所产生的有效按键.举例而言,按下按键 <A> 输出“字符a”,

实验八--排序算法

实验八 排序算法的实现 一. 实验目的: 熟练掌握常用的直接插入排序.简单选择排序.冒泡排序等算法. 深入理解各种查找排序的结构特点及各算法之间的区别. 二. 实验内容: 采用直接插入排序.简单选择排序.冒泡排序等常用的排序算法实现给数据的排序. 各种排序算法的结构特点及各算法之间的区别. 代码如下: RecordNode.java package sort; import java.util.Scanner; public class RecordNode { public int key ;

实验八 应用层协议Ⅱ-FTP协议分析

实验八 应用层协议Ⅱ-FTP协议分析 一.实验目的 1.掌握FTP协议的实现原理. 2.了解控制通道和数据通道. 二.实验内容 用WareShark追踪ftp连接. 1.三次握手 2.ftp服务器回发"准备就绪". 3.客户端给服务器发送用户名,服务器回发ACK,并向服务器请求密码. 4.客户端给服务器发送密码,密码为"[email protected]",服务器回发ACK并告诉客户端登录       成功. 三.实验总结 这次实验体验了FTP文件传输的过程,从三次