实验(5)、SRAM搬移到SDRAM中运行

实验目的:编写代码让SRAM中代码搬移到SDRAM上运行跑马灯,并能顺利进入中断

实验描述:本工程用makefile告诉编译器,要到0x30000000上去找代码,而代码在SRAM中运行的时候,就把所有程序都拷贝到makefile中指定的位置,然后通过绝对地址跳转,跳入到函数符号为main的绝对地址上运行。

实验文件:head.S、init.c、main.c、interrupt.c、Makefile、s3c24xx.h

(1)head.S

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

@ File:head.S

@功能:初始化,设置中断模式、系统模式的栈,设置好中断处理函数

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

.extern main

.text

.global _start

_start:

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

@中断向量,本程序中,除Reset和HandleIRQ外,其他异常都没有使用

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

@0x00地址处的指令为"b Reset",在系统复位后,这条指令将跳去执行标号"Reset"开始的代码

b Reset

@0x04:未定义指令终止模式的向量地址

HandleUndef:

b HandleUndef

@0x08:管理模式的向量地址,通过SWI指令进入此模式

HandleSWI:

b HandleSWI

@0x0c:指令预取终止导致的异常的向量地址

HandlePrefetchAbort:

b HandlePrefetchAbort

@0x10:数据访问终止导致的异常的向量地址

HandleDataAbort:

b HandleDataAbort

@0x14:保留

HandleNotUsed:

b HandleNotUsed

@0x18:中断模式的向量地址

b HandleIRQ

@0x1c:快中断模式的向量地址

HandleFIQ:

b HandleFIQ

Reset:

ldr sp,=4096

@设置栈指针,以下都是C函数,调用前需要设好栈。栈是用 @来保存C函数的变量和返回地址

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

msr cpsr_c,#0xd2 @进入中断模式

ldr sp,=3072 @设置中断模式的栈指针,这里的sp寄存器是sp_irq

msr cpsr_c,#0xdf @进入系统模式

ldr sp,=4096 @设置系统模式的栈指针,这里的sp寄存器是sp_sys

@其实复位之后,CPU就处于系统模式

@前面的"ldr sp,=4096"完成同样的功能,此句可省略

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

bl copy_steppingstone_to_sdram @复制代码到SDRAM中

ldr pc, =on_sdram @跳到SDRAM中继续执行

on_sdram:

msr cpsr_c, #0xd2 @进入中断模式

ldr sp, =4096 @设置中断模式栈指针

msr cpsr_c, #0xdf @进入系统模式

ldr sp, =0x34000000 @设置系统模式栈指针,

bl init_led @初始化LED的GPIO管脚,在init.c中

bl init_irq @调用中断初始化函数,在init.c中

msr cpsr_c, #0x5f @设置I-bit=0,开IRQ中断

ldr lr, =halt_loop @设置返回地址

ldr pc, =main @调用main函数

halt_loop:

b halt_loop

HandleIRQ: @HandleIRQ开始的代码用于处理中断

sub lr,lr,#4 @计算中断处理完毕后的返回地址

stmdb sp!,{r0-r12,lr} @保存使用到的寄存器

@注意,此时的sp是中断模式的sp

@初始值是上面设置的3072

ldr lr,=int_return @设置调用ISR即EINT_Handle函数后的返回地址

bl EINT_Handle @调用中断服务函数,在interrupt.c中

int_return:

ldmia sp!,{r0-r12,pc}^ @中断返回,^表示将spsr的值复制到cpsr

(2)init.c

#include "s3c24xx.h"

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

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

#define GPB7_out (1<<(7*2))

#define GPB8_out (1<<(8*2))

#define GPB0_timer0 (2<<(0*2))

#define GPB5_msk (3<<(5*2))

#define GPB6_msk (3<<(6*2))

#define GPB7_msk (3<<(7*2))

#define GPB8_msk (3<<(8*2))

#define GPF0_eint (0x2<<(0*2))

#define GPF1_eint (0x2<<(1*2))

#define GPF2_eint (0x2<<(2*2))

#define GPF4_eint (0x2<<(4*2))

#define GPF0_msk (3<<(0*2))

#define GPF1_msk (3<<(1*2))

#define GPF2_msk (3<<(2*2))

#define GPF4_msk (3<<(4*2))

#define S3C2410_MPLL_200MHZ ((0x5c<<12)|(0x04<<4)|(0x00))

#define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02))

#define PCLK (50000000)

void disable_watch_dog(void)

{

WTCON = 0;

}

void init_led(void)

{

GPBCON&=~(GPB5_msk|GPB6_msk|GPB7_msk|GPB8_msk);

GPBCON|=GPB5_out|GPB6_out|GPB7_out|GPB8_out;

GPBDAT|=0x0f<<5;

}

void init_irq( )

{

GPFCON&=~(GPF0_msk|GPF1_msk|GPF2_msk|GPF4_msk);

GPFCON|=GPF0_eint|GPF1_eint|GPF2_eint|GPF4_eint;

EINTMASK&=~(1<<4);

PRIORITY&=(~(1<<6))&(~(1<<1))&(~(1<<0));

INTMSK &=(~(1<<0))&(~(1<<1))&(~(1<<2))&(~(1<<4));

}

void memsetup(void)

{

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

/*这个函数之所以这样赋值,而不是像前面的实验(比如mmu实验)那样将配置值

*写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到

* SDRAM之前就可以在steppingstone中运行

*/

/*存储控制器13个寄存器的值 */

p[0] = 0x22011110; //BWSCON

p[1] = 0x00000700; //BANKCON0

p[2] = 0x00000700; //BANKCON1

p[3] = 0x00000700; //BANKCON2

p[4] = 0x00000700; //BANKCON3

p[5] = 0x00000700; //BANKCON4

p[6] = 0x00000700; //BANKCON5

p[7] = 0x00018005; //BANKCON6

p[8] = 0x00018005; //BANKCON7

/* REFRESH,

* HCLK=12MHz: 0x008C07A3,

* HCLK=100MHz: 0x008C04F4

*/

p[9] = 0x008C04F4;

p[10] = 0x000000B1; //BANKSIZE

p[11] = 0x00000030; //MRSRB6

p[12] = 0x00000030; //MRSRB7

}

void copy_steppingstone_to_sdram(void)

{

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

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

while (pdwSrc < (unsigned int *)4096)

{

*pdwDest = *pdwSrc;

pdwDest++;

pdwSrc++;

}

}

(3)interrupt.c

#include "s3c24xx.h"

void EINT_Handle()

{

unsigned long oft = INTOFFSET;

switch( oft )

{

case 0:

{

GPBDAT=~(0x0f<<5);

//GPBDAT=~(1<<8);

break;

}

case 1:

{

GPBDAT=~(0x0f<<5);

//GPBDAT=~(1<<5);

break;

}

case 2:

{

GPBDAT=~(0x0f<<5);

//GPBDAT=~(1<<7);

break;

}

case 4:

{

GPBDAT=~(0x0f<<5);

//GPBDAT=~(1<<6);

break;

}

default:

break;

}

//清中断

if( oft == 4 )

EINTPEND = (1<<4); // EINT8_23合用IRQ5

SRCPND = 1<<oft;

INTPND = 1<<oft;

}

(4)main.c

#define GPBCON (*(volatile unsigned long *)0x56000010)

//以下为根据S3C2440开发手册的GPBDAT寄存器定义的宏,由于其可变,需设置宏为volatile,该寄存器用于设置输出数据

#define GPBDAT (*(volatile unsigned long *)0x56000014)

//以下宏为什么要移动2*5呢,原因就是GPBCON以两位控制GPBIO的一位输出,00输入,01输出,详见S3C2440手册

#define led1out (1<<2*5)

//实验中跑马灯有四个灯要循环,故此需要四组输出控制来配置GPBCON

#define led2out (1<<2*6)

#define led3out (1<<2*7)

#define led4out (1<<2*8)

void delay(int i)

{

while(i--); //循环延时,如果没有倍频,则机器周期为1/12M ,这里的循环翻译成汇编不止一条机器指令,特别注意

}

int main()

{

GPBCON |= led1out|led2out|led3out|led4out; //配置GPB5到GPB8端口输出属性

int i=0;

while(1)

{

GPBDAT = ~(1<<(i%4+5)); //亮灯

delay(100000);//延时

i++; //换灯变量

}

return 0;

}

(5)s3c24xx.h

/* WOTCH DOG register */

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

/* SDRAM regisers */

#define MEM_CTL_BASE 0x48000000

#define SDRAM_BASE 0x30000000

/* NAND Flash registers */

#define NFCONF (*(volatile unsigned int *)0x4e000000)

#define NFCMD (*(volatile unsigned char *)0x4e000004)

#define NFADDR (*(volatile unsigned char *)0x4e000008)

#define NFDATA (*(volatile unsigned char *)0x4e00000c)

#define NFSTAT (*(volatile unsigned char *)0x4e000010)

/*GPIO registers*/

#define GPBCON (*(volatile unsigned long *)0x56000010)

#define GPBDAT (*(volatile unsigned long *)0x56000014)

#define GPFCON (*(volatile unsigned long *)0x56000050)

#define GPFDAT (*(volatile unsigned long *)0x56000054)

#define GPFUP(*(volatile unsigned long *)0x56000058)

#define GPGCON (*(volatile unsigned long *)0x56000060)

#define GPGDAT (*(volatile unsigned long *)0x56000064)

#define GPGUP (*(volatile unsigned long *)0x56000068)

#define GPHCON (*(volatile unsigned long *)0x56000070)

#define GPHDAT (*(volatile unsigned long *)0x56000074)

#define GPHUP (*(volatile unsigned long *)0x56000078)

/*UART registers*/

#define ULCON0 (*(volatile unsigned long *)0x50000000)

#define UCON0 (*(volatile unsigned long *)0x50000004)

#define UFCON0 (*(volatile unsigned long *)0x50000008)

#define UMCON0 (*(volatile unsigned long *)0x5000000c)

#define UTRSTAT0(*(volatile unsigned long *)0x50000010)

#define UTXH0 (*(volatile unsigned char *)0x50000020)

#define URXH0 (*(volatile unsigned char *)0x50000024)

#define UBRDIV0 (*(volatile unsigned long *)0x50000028)

/*interrupt registes*/

#define SRCPND (*(volatile unsigned long *)0x4A000000)

#define INTMOD (*(volatile unsigned long *)0x4A000004)

#define INTMSK (*(volatile unsigned long *)0x4A000008)

#define PRIORITY (*(volatile unsigned long *)0x4A00000c)

#define INTPND (*(volatile unsigned long *)0x4A000010)

#define INTOFFSET (*(volatile unsigned long *)0x4A000014)

#define SUBSRCPND (*(volatile unsigned long *)0x4A000018)

#define INTSUBMSK (*(volatile unsigned long *)0x4A00001c)

/*external interrupt registers*/

#define EINTMASK (*(volatile unsigned long *)0x560000a4)

#define EINTPEND (*(volatile unsigned long *)0x560000a8)

/*clockregisters*/

#define LOCKTIME (*(volatile unsigned long*)0x4c000000)

#define MPLLCON (*(volatile unsigned long*)0x4c000004)

#define UPLLCON (*(volatile unsigned long*)0x4c000008)

#define CLKCON (*(volatile unsigned long*)0x4c00000c)

#define CLKSLOW (*(volatile unsigned long*)0x4c000010)

#define CLKDIVN (*(volatile unsigned long*)0x4c000014)

/*PWM&Timerregisters*/

#define TCFG0 (*(volatile unsigned long*)0x51000000)

#define TCFG1 (*(volatile unsigned long*)0x51000004)

#define TCON (*(volatile unsigned long*)0x51000008)

#define TCNTB0 (*(volatile unsigned long*)0x5100000c)

#define TCMPB0 (*(volatile unsigned long*)0x51000010)

#define TCNTO0 (*(volatile unsigned long*)0x51000014)

#define GSTATUS1 (*(volatile unsigned long*)0x560000B0)

#define U16 unsigned short int

(6)Makefile

ARCH = arm

-mcpu=s3c2440

objs := head.o init.o interrupt.o main.o

int.bin: $(objs)

arm-linux-ld -Ttext 0x30000000 -o int_elf $^

arm-linux-objcopy -O binary -S int_elf int.bin

arm-linux-objdump -D -m arm int_elf > int.dis

%.o:%.c

arm-linux-gcc -nostdlib -g -c -o $@ $<

%.o:%.S

arm-linux-gcc -nostdlib -g -c -o $@ $<

clean:

rm -f int.bin int_elf int.dis *.o

Q&A:

(1)思考1:本实验中,连接地址是0x30000000,请问,如果将这个连接地址取成0x00000000,是否能顺利跳入到main函数?如果不能顺利跳入主函数,如何修改?带着这个问题再对比实验6,然后,请编程实现,当makefile中定义地址为0x00000000,顺利跳入主函数,只修改一个地方,请找出来并做修改。

(2)思考2:本实验中,连接地址是0x30000000,请问,如果将这个连接地址取成0x00000000,是否能顺利跳入中断?(答案是能),为什么?

(3)代码中红色加深的bl EINT_Handle,请用一句等效的代码代替(提示:用绝对地址跳转,即用位置有关的跳转),运行结果不变。请问替换前和替换后,虽然实验现象一致,是否有什么本质的不同?



results matching ""

    No results matching ""

    results matching ""

      No results matching ""