TQ210裸机编程(十)——代码重定位
关于重定位的理论知识推荐大家观看韦东山的视频《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