TQ210裸机编程(十)——代码重定位

TQ210裸机编程(10)——代码重定位

关于重定位的理论知识推荐大家观看韦东山的视频《6410裸板视频》中的第5章(位置无关码、重定位)。

下载地址:http://pan.baidu.com/share/link?uk=2520074993&shareid=483439#dir/path=%2F%E9%9F%A6%E4%B8%9C%E5%B1%B1Linux%E8%A7%86%E9%A2%91%E7%AC%AC1%E6%9C%9F_S3C6410%E8%A3%B8%E6%9D%BF

在S5PV210上的实验方法:烧写程序到SD的第1块,从SD启动,程序的链接地址为0xD0024000,在程序中将程序拷贝到0xD0024000地址处,然后跳转到main函数去执行。

运行效果如下:

before relocate

after relocate
t = 0
t = 1
t = 2
t = 3
t = 4
t = 5
8ððàÿÿÿÿÿÿÿø

在重定位之前,用自己实现的puts函数打印字符串“before relocate”,重定位后,循环打印t的值。

注意这里在重定位前不能使用printf("before relocate")这样的操作,因为字符串"before relocate"是放在数据段中的,而程序中访问数据段的内容使用的是链接地址(0xD0024000之后),而这时程序还位于0xD0020010。具体怎么处理见代码。


start.S

.global br_str 				/* 声明全局标号 */
.global ar_str
.global _start
_start:
	bl clock_init			/* 时钟初始化 */
	bl uart_init			/* 串口初始化 */

	adr r0, br_str			/* 取得br_str的当前地址(重定位之前) */
	/* 调用C函数before_relocate,接收一个参数 */
	bl before_relocate

	/* 重定位 */
	adr r0, _start 			/* 相对寻址:取得_start的当前地址0xD0020010 */
	ldr r1, =_start 		/* 绝对寻址:取得_start的链接地址0xD0024000 */
	ldr r2, =bss_start		/* bss段的起始地址 */
	cmp r0, r1				/* 如果取得_start的当前地址和链接地址相等,则说明程序已经位于它的链接地址 */
	beq clean_bss
	
copy_loop:
	ldr r3, [r0], #4    // 源
	str r3, [r1], #4	// 目的
	cmp r1, r2
	bne copy_loop


	/* 清bss段 */
clean_bss:
	ldr r0, =bss_start					
	ldr r1, =bss_end
	cmp r0, r1
	beq relocate_done
	/* 为什么这里要比较呢?
	** 因为如果相等,则bss段没有数据,而这时如果进行下面的清bss段操作
	** 首先执行“str r2, [r0], #4”后,r0加4,再执行“cmp r0, r1”就不相等了,这样一直循环。
	*/

	mov r2, #0
1:
	str r2, [r0], #4
	cmp r0, r1
	bne 1b			/* b是back的意思,代表向后跳转;如果向前跳转则使用f意思是forward */

	/* 跳转 */
relocate_done:			 
	ldr pc, =main

halt:
	b halt

/* 定义一个字符串:.asciz会自动添加一个'\0',而ascii不会 */
br_str:
	.asciz "\nbefore relocate\n"



types.h

#ifndef TYPES_H_
#define TYPES_H_

typedef volatile char 			s8;
typedef volatile short 			s16;
typedef volatile int 			s32;

typedef volatile unsigned char 	u8;
typedef volatile unsigned short u16;
typedef volatile unsigned int 	u32;

#endif

clock.c

#include <stdarg.h>
#include "types.h"

#define GPA0CON		*((volatile unsigned int *)0xE0200000)
#define ULCON0 		*((volatile unsigned int *)0xE2900000)
#define UCON0 		*((volatile unsigned int *)0xE2900004)
#define UFCON0 		*((volatile unsigned int *)0xE2900008)
#define UTRSTAT0 	*((volatile unsigned int *)0xE2900010)
#define UTXH0  		*((volatile unsigned int *)0xE2900020)
#define URXH0 		*((volatile unsigned int *)0xE2900024)
#define UBRDIV0 	*((volatile unsigned int *)0xE2900028)
#define UDIVSLOT0	*((volatile unsigned int *)0xE290002C)

/*
** UART0初始化
*/
void uart_init()
{
	/*
	** 配置GPA0_0为UART_0_RXD
	** 配置GPA0_1为UART_0_TXD
	*/
	GPA0CON &= ~0xFF;
	GPA0CON |= 0x22;

	/* 8-bits/One stop bit/No parity/Normal mode operation */
	ULCON0 = 0x3 | (0 << 2) | (0 << 3) | (0 << 6);

	/* Interrupt request or polling mode/Normal transmit/Normal operation/PCLK/*/
	UCON0 = 1 | (1 << 2) | (0 << 10);

	/* 静止FIFO */
	UFCON0 = 0;

	/*
	** 波特率计算:115200bps
	** PCLK = 66MHz
	** DIV_VAL = (66000000/(115200 x 16))-1 = 35.8 - 1 = 34.8
	** UBRDIV0 = 34(DIV_VAL的整数部分)
	** (num of 1's in UDIVSLOTn)/16 = 0.8
	** (num of 1's in UDIVSLOTn) = 12
	** UDIVSLOT0 = 0xDDDD (查表)
	*/
	UBRDIV0 = 34;
	UDIVSLOT0 = 0xDDDD;
}

/* 输出一字节的数据到终端 */
void uart_send_byte(u8 byte)
{
	while (!(UTRSTAT0 & (1 << 2)));	/* 等待发送缓冲区为空 */
	UTXH0 = byte;					/* 发送一字节数据 */		
}

/* 从终端接收一字节数据 */
u8 uart_recv_byte()
{
	while (!(UTRSTAT0 & 1));	/* 等待接收缓冲区有数据可读 */
	return URXH0;				/* 接收一字节数据 */		
}

/* 打印字符c到终端 */
void putchar(u8 c)
{
	uart_send_byte(c);
	if (c == '\n')
		uart_send_byte('\r');
}

/* 打印字符串s到终端 */
void puts(s8 *s)
{
	s8 *p = s;
	while (*p)
		putchar(*p++);
}

/* 打印整数v到终端 */
void put_int(u32 v)
{
	int i;
	u8 a[10];
	u8 cnt = 0;

	if (v == 0)
	{
		putchar('0');
		return;
	}

	while (v)
	{
		a[cnt++] = v % 10;
		v /= 10; 
	}

	for (i = cnt - 1; i >= 0; i--)
		putchar(a[i] + 0x30);
	/*
	** 整数0-9的ASCII分别为0x30-0x39
	*/
}

/* 将一字节十六进制数按十六进制显示打印到终端 */
void put_hex(u8 v, u8 small)
{
	/* 注意:必须用volatile修饰,否则会出错 */
	u8 h, l;		/* 高4位和第4位(这里按二进制算) */
	char *hex1 = "0123456789abcdef";		/* 这里放在数据段中 */
	char *hex2 = "0123456789ABCDEF";

	h = v >> 4;
	l = v & 0x0F;

	if (small)	/* 小写 */
	{
		putchar(hex1[h]);	/* 高4位 */
		putchar(hex1[l]);	/* 低4位 */
	}
	else		/* 大写 */
	{
		putchar(hex2[h]);	/* 高4位 */
		putchar(hex2[l]);	/* 低4位 */
	}
}

/* 将int型整数按16进制打印到终端 */
void put_int_hex(u32 v, u8 small)
{
	if (v >> 24)
	{
		put_hex(v >> 24, small);
		put_hex((v >> 16) & 0xFF, small);
		put_hex((v >> 8) & 0xFF, small);
		put_hex(v & 0xFF, small);
	}
	else if ((v >> 16) & 0xFF)
	{
		put_hex((v >> 16) & 0xFF, small);
		put_hex((v >> 8) & 0xFF, small);
		put_hex(v & 0xFF, small);
	}
	else if ((v >> 8) & 0xFF)
	{
		put_hex((v >> 8) & 0xFF, small);
		put_hex(v & 0xFF, small);
	}
	else
		put_hex(v & 0xFF, small);
}

/* 格式化输出到终端 */
int printf(const char *fmt, ...)
{
	va_list ap;
	s8 c;
	s8 *s;
	u32 d;
	u8 small;

	va_start(ap, fmt);
	while (*fmt)
	{
		small = 0;
		c = *fmt++;
		if (c == '%')
		{
			switch (*fmt++)
			{
			case 'c':              /* char */
				c = (char) va_arg(ap, int);
				putchar(c);
				break;
			case 's':              /* string */
				s = va_arg(ap, char *);
				puts(s);
				break;
			case 'd':              /* int */
			case 'u':
				d = va_arg(ap, int);
				put_int(d);
				break;
			case 'x':
				small = 1;	// small
			case 'X':
				d = va_arg(ap, int);
				put_int_hex(d, small);
				break;
			}
		}
		else
			putchar(c);
	}
	va_end(ap);
}

#define APLLCON0		*((volatile unsigned int *)0xE0100100)
#define MPLLCON			*((volatile unsigned int *)0xE0100108)
#define EPLLCON0		*((volatile unsigned int *)0xE0100110)
#define VPLLCON			*((volatile unsigned int *)0xE0100120)
#define CLK_SRC0 		*((volatile unsigned int *)0xE0100200)
#define CLK_DIV0		*((volatile unsigned int *)0xE0100300)
#define CLK_DIV1		*((volatile unsigned int *)0xE0100304)
#define CLK_DIV2		*((volatile unsigned int *)0xE0100308)
#define CLK_DIV3		*((volatile unsigned int *)0xE010030C)

void clock_init()
{
	/* 1、设置PLL_LOCK寄存器(这里使用默认值) */
	/* 2、设置PLL_CON寄存器(使用芯片手册推荐的值) */
	APLLCON0	= (1 << 0) | (3 << 8) | (125 << 16) | (1 << 31);	/* FOUTAPLL = 1000MHz */
	MPLLCON 	= (1 << 0) | (12 << 8) | (667 << 16) | (1 << 31);	/* FOUTMPLL = 667MHz */
	EPLLCON0 	= (1 << 0) | (12 << 8) | (667 << 16) | (1 << 31);	/* FOUTEPLL = 96MHz */
	VPLLCON 	= (3 << 0) | (6 << 8) | (108 << 16) | (1 << 31);	/* FOUTVPLL = 54MHz */
	
	/* 3、选择PLL为时钟输出 */
	/* MOUT_MSYS = SCLKAPLL = 1000MHz
	** MOUT_DSYS = SCLKMPLL = 667MHz
	** MOUT_PSYS = SCLKMPLL = 667MHz
	*/
	CLK_SRC0 = (1 << 0) | (1 << 4) | (1 << 8) | (1 << 12);
	
	/* 4、设置系统时钟分频值 */
	/* freq(ARMCLK) = MOUT_MSYS / (APLL_RATIO + 1) = 1000MHz / (0 + 1) = 1000MHz
	** freq(HCLK_MSYS) = ARMCLK / (HCLK_MSYS_RATIO + 1) = 1000MHz / (4 + 1) = 200MHz
	** freq(PCLK_MSYS) = HCLK_MSYS / (PCLK_MSYS_RATIO + 1) = 200MHz / (1 + 1) = 100MHz
	** freq(HCLK_DSYS) = MOUT_DSYS / (HCLK_DSYS_RATIO + 1) = 667 / (3 + 1) = 166MHz
	** freq(PCLK_DSYS) = HCLK_DSYS / (PCLK_DSYS_RATIO + 1) = 166 / (1 + 1) = 83MHz
	** freq(HCLK_PSYS) = MOUT_PSYS / (HCLK_PSYS_RATIO + 1) = 667 / (4 + 1) = 133MHz
	** freq(PCLK_PSYS) = HCLK_PSYS / (PCLK_PSYS_RATIO + 1) = 133 / (1 + 1) = 66MHz
	*/
	CLK_DIV0 = (0 << 0) | (4 << 8) | (1 << 12) | (3 << 16) | (1 << 20) | (4 << 24) | (1 << 28);
}


uart.c

#include <stdarg.h>
#include "types.h"

#define GPA0CON		*((volatile unsigned int *)0xE0200000)
#define ULCON0 		*((volatile unsigned int *)0xE2900000)
#define UCON0 		*((volatile unsigned int *)0xE2900004)
#define UFCON0 		*((volatile unsigned int *)0xE2900008)
#define UTRSTAT0 	*((volatile unsigned int *)0xE2900010)
#define UTXH0  		*((volatile unsigned int *)0xE2900020)
#define URXH0 		*((volatile unsigned int *)0xE2900024)
#define UBRDIV0 	*((volatile unsigned int *)0xE2900028)
#define UDIVSLOT0	*((volatile unsigned int *)0xE290002C)

/*
** UART0初始化
*/
void uart_init()
{
	/*
	** 配置GPA0_0为UART_0_RXD
	** 配置GPA0_1为UART_0_TXD
	*/
	GPA0CON &= ~0xFF;
	GPA0CON |= 0x22;

	/* 8-bits/One stop bit/No parity/Normal mode operation */
	ULCON0 = 0x3 | (0 << 2) | (0 << 3) | (0 << 6);

	/* Interrupt request or polling mode/Normal transmit/Normal operation/PCLK/*/
	UCON0 = 1 | (1 << 2) | (0 << 10);

	/* 静止FIFO */
	UFCON0 = 0;

	/*
	** 波特率计算:115200bps
	** PCLK = 66MHz
	** DIV_VAL = (66000000/(115200 x 16))-1 = 35.8 - 1 = 34.8
	** UBRDIV0 = 34(DIV_VAL的整数部分)
	** (num of 1's in UDIVSLOTn)/16 = 0.8
	** (num of 1's in UDIVSLOTn) = 12
	** UDIVSLOT0 = 0xDDDD (查表)
	*/
	UBRDIV0 = 34;
	UDIVSLOT0 = 0xDDDD;
}

/* 输出一字节的数据到终端 */
void uart_send_byte(u8 byte)
{
	while (!(UTRSTAT0 & (1 << 2)));	/* 等待发送缓冲区为空 */
	UTXH0 = byte;					/* 发送一字节数据 */		
}

/* 从终端接收一字节数据 */
u8 uart_recv_byte()
{
	while (!(UTRSTAT0 & 1));	/* 等待接收缓冲区有数据可读 */
	return URXH0;				/* 接收一字节数据 */		
}

/* 打印字符c到终端 */
void putchar(u8 c)
{
	uart_send_byte(c);
	if (c == '\n')
		uart_send_byte('\r');
}

/* 打印字符串s到终端 */
void puts(s8 *s)
{
	s8 *p = s;
	while (*p)
		putchar(*p++);
}

/* 打印整数v到终端 */
void put_int(u32 v)
{
	int i;
	u8 a[10];
	u8 cnt = 0;

	if (v == 0)
	{
		putchar('0');
		return;
	}

	while (v)
	{
		a[cnt++] = v % 10;
		v /= 10; 
	}

	for (i = cnt - 1; i >= 0; i--)
		putchar(a[i] + 0x30);
	/*
	** 整数0-9的ASCII分别为0x30-0x39
	*/
}

/* 将一字节十六进制数按十六进制显示打印到终端 */
void put_hex(u8 v, u8 small)
{
	/* 注意:必须用volatile修饰,否则会出错 */
	u8 h, l;		/* 高4位和第4位(这里按二进制算) */
	char *hex1 = "0123456789abcdef";		/* 这里放在数据段中 */
	char *hex2 = "0123456789ABCDEF";

	h = v >> 4;
	l = v & 0x0F;

	if (small)	/* 小写 */
	{
		putchar(hex1[h]);	/* 高4位 */
		putchar(hex1[l]);	/* 低4位 */
	}
	else		/* 大写 */
	{
		putchar(hex2[h]);	/* 高4位 */
		putchar(hex2[l]);	/* 低4位 */
	}
}

/* 将int型整数按16进制打印到终端 */
void put_int_hex(u32 v, u8 small)
{
	if (v >> 24)
	{
		put_hex(v >> 24, small);
		put_hex((v >> 16) & 0xFF, small);
		put_hex((v >> 8) & 0xFF, small);
		put_hex(v & 0xFF, small);
	}
	else if ((v >> 16) & 0xFF)
	{
		put_hex((v >> 16) & 0xFF, small);
		put_hex((v >> 8) & 0xFF, small);
		put_hex(v & 0xFF, small);
	}
	else if ((v >> 8) & 0xFF)
	{
		put_hex((v >> 8) & 0xFF, small);
		put_hex(v & 0xFF, small);
	}
	else
		put_hex(v & 0xFF, small);
}

/* 格式化输出到终端 */
int printf(const char *fmt, ...)
{
	va_list ap;
	s8 c;
	s8 *s;
	u32 d;
	u8 small;

	va_start(ap, fmt);
	while (*fmt)
	{
		small = 0;
		c = *fmt++;
		if (c == '%')
		{
			switch (*fmt++)
			{
			case 'c':              /* char */
				c = (char) va_arg(ap, int);
				putchar(c);
				break;
			case 's':              /* string */
				s = va_arg(ap, char *);
				puts(s);
				break;
			case 'd':              /* int */
			case 'u':
				d = va_arg(ap, int);
				put_int(d);
				break;
			case 'x':
				small = 1;	// small
			case 'X':
				d = va_arg(ap, int);
				put_int_hex(d, small);
				break;
			}
		}
		else
			putchar(c);
	}
	va_end(ap);
}

main.c

#include "uart.h"
#include "types.h"

void delay(u32 t)
{
	u32 t2 = 0xFFFF;
	while (t--)
		for (; t2; t2--);
}

void before_relocate(u32 addr)
{
	char *p = (char *)addr;
	puts(p);
}

int main()
{
	u32 t = 0;
	printf("\nafter relocate\n\n");
	while (1)
	{
		printf("t = %d\n", t++);
		delay(1000000);
	}
	return 0;
}

link.lds(链接脚本)

SECTIONS
{
	. = 0xD0024000;
	
	.text : {
		start.o
		* (.text)
	}
    		
	.data : {
		* (.data)
	}
	
	bss_start = .; 
	.bss : {
		* (.bss)
	}
	
	bss_end  = .;	
}

Makefile

uart.bin: start.o clock.o uart.o main.o
    arm-linux-ld -Tlink.lds -o uart.elf $^
    arm-linux-objcopy -O binary uart.elf $@
    arm-linux-objdump -D uart.elf > uart.dis
    
%.o : %.c
    arm-linux-gcc -c $< -o $@
%.o : %.S
    arm-linux-gcc -c $< -o $@
    
clean:
    rm *.o *.elf *.bin *.dis

烧写过程见《TQ210裸机编程(5)——系统时钟配置

转载请注明来源:http://blog.****.net/zjhsucceed_329/

QQ:783692389