实验(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,请用一句等效的代码代替(提示:用绝对地址跳转,即用位置有关的跳转),运行结果不变。请问替换前和替换后,虽然实验现象一致,是否有什么本质的不同?