看门狗
电子设备会跑飞或者死机,需要设备自动复位,看门狗是SoC内部定时器,规定时间内需要重新置位,如果没有系统会被强制复位
WTCON(0xE2700000),bit5是开关:0关,1开
汇编设置栈和调用C
C运行过程中局部变量需要栈,如果汇编没有设置栈地址,局部变量会落空,程序崩溃
系统在复位后默认是SVC模式,设置栈时不设置所有栈而是设置自己模式下的栈
CPU启动,外部DRRAM未初始化,内部SRAM使用,SRAM内存做SVC栈
SVC栈置为0xd0037D80(满减栈)
初始化iCache
cache是内存高速缓存,打开icache后程序运行加快
容量:CPU < 寄存器 < cache < DDR
速度:CPU > 寄存器 > cache > DDR
汇编读写cp15开关icache
mrc p15,0,r0,c1,c0,0 // 读出cp15的c1到r0中
bic r0, r0, #(1<<12) // bit12 置0 关icache
orr r0, r0, #(1<<12) // bit12 置1 开icache
mcr p15,0,r0,c1,c0,0
重定位和链接
位置无关编码(PIC):汇编源文件编码成二进制可执行程序时编码方式与位置(内存地址)无关
位置有关编码:汇编源码编码成二进制可执行程序后和内存地址是有关
链接地址:链接时指定的地址,Makefile中-Ttext或链接脚本指定
运行地址:程序实际运行地址,运行时被加载到的内存位置
位置有关代码执行时运行地址和编译链接地址必须相同
重定位是在运行地址处执行位置无关码,位置无关码从运行地址拷贝到链接地址处
注意:210运行时地址是0xd0020010,指定的链接地址与运行地址不同时,内部SRAM射到0x0地址,故程序运行正常
长跳转与短跳转
长跳转:ldr pc, = func
短跳转:bl func
链接地址和运行地址相同,短跳转和长跳转无差异
链接地址和运行地址不同,短跳转在运行地址,长跳转在链接地址处
adr与伪指令ldr
ldr是长加载,adr是短加载
adr加载运行时地址,ldr加载链接地址
adr和ldr可以判断是否需要重定位
程序段
代码段:(.text)文本段
数据段:(.data)
bss段: (.bss),ZI段
自己定义段
链接脚本
链接脚本:段名,地址(作为链接地址的内存地址)
SECTIONS {} 整个链接脚本
. 当前位置
= 赋值
SoC时钟
时钟是同步工作系统的同步节拍,SoC内部器件需要同步时钟系统指挥
210获得时钟:外部晶振+内部时钟发生器+内部PLL产生高频时钟(1GHz、1.2GHz)+内部分频器分频
210的时钟
时钟域:MSYS、DSYS、PSYS
MSYS: CPU(Cortex-A8内核)、DRAM控制器(DMC0和DMC1)、IRAM&IROM等
DSYS: 都是和视频显示、编解码等有关的模块等
PSYS: 和内部的各种外设时钟有关,譬如串口、SD接口、I2C、AC97、USB等
时钟来源:晶振+时钟发生器+PLL+分频电路
210外部有4个晶振接口,接晶振后上电相应模块振荡产生原始时钟,原始时钟经过筛选进入PLL电路生成高频时钟,再经分频到各模块
PLL
PLL:APLL、MPLL、VPLL
APLL:Cortex-A8内核 MSYS域
MPLL&EPLL:DSYS PSYS
VPLL:Video视频相关模块
210时钟域
MSYS
ARMCLK:cpu内核时钟(主频)
HCLK_MSYS:MSYS域高频时钟(DMC0和DMC1)
PCLK_MSYS:MSYS域低频时钟
HCLK_IMEM:iMEM(iROM和iRAM)
DSYS
HCLK_DSYS:DSYS域高频时钟
PCLK_DSYS:DSYS域低频时钟
PSYS
HCLK_PSYSPSYS域的高频时钟
PCLK_PSYS:PSYS域的低频时钟
SCLK_ONENAND
注意:210内部外设接在(内部AMBA总线)总线上,AMBA总线高频分支AHB,低频分支APB
时钟典型值
210上电默认外部晶振+内部时钟发生器产生24MHz时钟给ARMCLK,iROM初始化时钟系统
freq(ARMCLK) = 1000 MHz
freq(HCLK_MSYS) = 200 MHz
freq(HCLK_IMEM) = 100 MHz
freq(PCLK_MSYS) = 100 MHz
freq(HCLK_DSYS) = 166 MHz
freq(PCLK_DSYS) = 83 MHz
freq(HCLK_PSYS) = 133 MHz
freq(PCLK_PSYS) = 66 MHz
freq(SCLK_ONENAND) = 133 MHz, 166 MHz
时钟设置寄存器
锁存寄存器,频率设置寄存器(CON、SRC、DIV,CON决定PLL倍频,SRC决定来源,DIV决定分频),其他寄存器(开关控制器,开关和分频状态寄存器)
锁存寄存器:xPLL_LOCK:控制PLL锁存周期
频率设置寄存器
xPLL_CON/xPLL_CON0/xPLL_CON1:打开/关闭PLL电路,设置PLL倍频参数,查看PLL锁定状态
CLK_SRCn(n:0~6):设置时钟来源(MUX开关)
CLK_DIVn:各模块分频器参数配置
其他寄存器
CLK_SRC_MASKn:MUX(n选1),默认进开关开放
CLK_GATE_x:分频出开关控制
CLK_DIV_STATn和CLK_MUX_STATn:查看DIV和MUX状态(完成或进行)
时钟设置
选择不使用PLL,24MHz原始时钟接入绕过APLL(走FINAPLL)
设置锁定时间,默认0x0FFF,时钟系统有变化时缓冲时间
设置分频系统
设置PLL的倍频,ARMCLK(1GHz)
打开PLL
寄存器设置
CLK_SRC设置MUX开关,设置全0(bit0和bit4为0),APLL和MPLL禁用
CLK_LOCK设置PLL锁定延时,推荐值为0xFFF或0xFFFF
CLK_DIV寄存器设置为0x14131440 00010100000100110001010001000000
PCLK_PSYS = HCLK_PSYS / 2
HCLK_PSYS = MOUT_PSYS / 5
PCLK_DSYS = HCLK_DSYS / 2
HCLK_DSYS = MOUT_DSYS / 4
PCLK_MSYS = HCLK_MSYS /4
HCLK_MSYS = ARMCLK / 5
SCLKA2M = SCLKAPLL /5
ARMCLK = MOUT_MSYS / 1
APLL和MPLL设置M、P、S
LED
demo:
关看门狗,初始化栈,初始化icache,初始化时钟,重加载方式实现流水灯
代码示例:
start.S
#define WTCON 0xE2700000
#define SVC_STACK 0xd0037d80
.global _start
_start:
//close WatchDog
ldr r0,=WTCON
ldr r1,=0x0
str r1,[r0]
//init SVC stack
ldr sp,=SVC_STACK
//init clock
bl init_clock
//init icache
mrc p15,0,r0,c1,c0,0
bic r0,r0,#(1<<12) //close icache
orr r0,r0,#(1<<12) //open icache
mcr p15,0,r0,c1,c0,0
//judge reload
adr r0,_start
ldr r1,=_start
ldr r2,=bss_start
cmp r0,r1
beq clear_bss
//reload
copy_operation_to_link:
ldr r3,[r0],#4
str r3,[r1],#4
cmp r1,r2
bne copy_operation_to_link
//judge bss
ldr r0,=bss_start
ldr r1,=bss_end
mov r2,#0
cmp r0,r1
beq run_to_func
//clear bss
clear_bss:
str r2,[r0],#4
cmp r1,r0
bne clear_bss
run_to_func:
//long jump
ldr pc,=led_blink
//short jump
//bl led_blink
b .
led.c
#define GPJ0CON 0xE0200240
#define GPJ0DAT 0xE0200244
#define rGPJ0CON *((volatile unsigned int *) GPJ0CON)
#define rGPJ0DAT *((volatile unsigned int *) GPJ0DAT)
//dealy time
void dealy_time()
{
volatile unsigned int num = 900000;
while(num--);
}
//led blink
void led_blink()
{
//set jpio output
rGPJ0CON = 0x11111111;
while(1)
{
//led off
rGPJ0DAT = ((0<<3)|(0<<4)|(0<<5));
//dealy time
dealy_time();
//led on
rGPJ0DAT = ((1<<3)|(1<<4)|(1<<5));
//dealy time
dealy_time();
}
}
clock_init.c
//register base address
#define CLOCK_BASE 0xE0100000
//register offsect
#define APLL_LOCK_OFFSECT 0x0
#define MPLL_LOCK_OFFSECT 0x8
#define APLL_CON0_OFFSECT 0x0100
#define MPLL_CON_OFFSECECT 0x0108
#define CLOCK_SRC0_OFFSECT 0x0200
#define CLOCK_DIV0_OFFSECT 0x0300
//register define
#define rAPLL_LOCK *((volatile unsigned int *) (CLOCK_BASE + APLL_LOCK_OFFSECT))
#define rMPLL_LOCK *((volatile unsigned int *) (CLOCK_BASE + MPLL_LOCK_OFFSECT))
#define rAPLL_CON0 *((volatile unsigned int *) (CLOCK_BASE + APLL_CON0_OFFSECT))
#define rMPLL_CON *((volatile unsigned int *) (CLOCK_BASE + MPLL_CON_OFFSECECT))
#define rCLOCK_SRC0 *((volatile unsigned int *) (CLOCK_BASE + CLOCK_SRC0_OFFSECT))
#define rCLOCK_DIV0 *((volatile unsigned int *) (CLOCK_BASE + CLOCK_DIV0_OFFSECT))
//APLL_CON param
#define APLL_MDIV 0x007D
#define APLL_PDIV 0x0003
#define APLL_SDIV 0x0001
//MPLL_CON param
#define MPLL_MDIV 0x029B
#define MPLL_PDIV 0x000C
#define MPLL_SDIV 0x0001
//set PLL_CON param
#define SET_PLL_DIV(mdiv,pdiv,sdiv) (1<<31 |mdiv << 16 |pdiv << 8 |sdiv)
#define APLL_DIV SET_PLL_DIV(APLL_MDIV,APLL_PDIV,APLL_SDIV)
#define MPLL_DIV SET_PLL_DIV(MPLL_MDIV,MPLL_PDIV,MPLL_SDIV)
//init clock
void init_clock()
{
//close clock
rCLOCK_SRC0 = 0x0;
//set clock peroid
rAPLL_LOCK = 0x0000FFFF;
rMPLL_LOCK = 0x0000FFFF;
//set PLL is set multiple
//FOUT = MDIV*FIN/(PDIV*2^(SDIV-1))=0x7d*24/(0x3*2^(1-1))=1000 MHz
rAPLL_CON0 = APLL_DIV;
//FOUT = MDIV*FIN/(PDIV*2^SDIV)=0x29b*24/(0xc*2^1)= 667 MHz
rMPLL_CON = MPLL_DIV;
//set PLL_DIV is set diversion
rCLOCK_DIV0 = 0x14131440;
//open clock
rCLOCK_SRC0 = 0x10000111;
}
link.lds
SECTIONS
{
. = 0xd0024000;
.text :
{
start.o
*(.text)
}
.data :
{
*(.data)
}
bss_start = .;
.bss :
{
*(.bss)
}
bss_end = .;
}
Makefile
CC = arm-linux-gcc
LD = arm-linux-ld
OBJCOPY = arm-linux-objcopy
OBJDUMP = arm-linux-objdump
#预处理器的flag,flag就是编译器可选的选项
CPPFLAGS := -nostdlib -nostdinc
#C编译器的flag
CFLAGS := -Wall -O2 -fno-builtin
export CC LD OBJCOPY OBJDUMP CPPFLAGS CFLAGS
objs := start.o clock.o led.o
led.bin:$(objs)
$(LD) -Tlink.lds -o led.elf $^
$(OBJCOPY) -O binary led.elf led.bin
$(OBJDUMP) -D led.elf > led.dis
gcc mkv210.c -o mkv210
./mkv210 led.bin sd.bin
%.o:%.S
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c
%.o:%.c
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c
clean:
rm *.o *.elf *.bin *.dis mkv210 -f
mkv210.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUFSIZE (16*1024)
#define IMG_SIZE (16*1024)
#define SPL_HEADER_SIZE 16
//#define SPL_HEADER "S5PC110 HEADER "
#define SPL_HEADER "****************"
int main (int argc, char *argv[])
{
FILE *fp;
char *Buf, *a;
int BufLen;
int nbytes, fileLen;
unsigned int checksum, count;
int i;
if (argc != 3)
{
printf("Usage: %s <source file> <destination file>\n", argv[0]);
return -1;
}
//分配16K的buffer
BufLen = BUFSIZE;
Buf = (char *)malloc(BufLen);
if (!Buf)
{
printf("Alloc buffer failed!\n");
return -1;
}
memset(Buf, 0x00, BufLen);
//读源bin到buffer
fp = fopen(argv[1], "rb");
if( fp == NULL)
{
printf("source file open error\n");
free(Buf);
return -1;
}
//获取源bin长度
fseek(fp, 0L, SEEK_END); // 定位到文件尾
fileLen = ftell(fp); // 得到文件长度
fseek(fp, 0L, SEEK_SET); // 再次定位到文件头
//源bin长度不得超过16K-16byte
count = (fileLen < (IMG_SIZE - SPL_HEADER_SIZE))
? fileLen : (IMG_SIZE - SPL_HEADER_SIZE);
//buffer[0~15]存放"S5PC110 HEADER "
memcpy(&Buf[0], SPL_HEADER, SPL_HEADER_SIZE);
//读源bin到buffer[16]
nbytes = fread(Buf + SPL_HEADER_SIZE, 1, count, fp);
if ( nbytes != count )
{
printf("source file read error\n");
free(Buf);
fclose(fp);
return -1;
}
fclose(fp);
//计算校验和
a = Buf + SPL_HEADER_SIZE;
for(i = 0, checksum = 0; i < IMG_SIZE - SPL_HEADER_SIZE; i++)
checksum += (0x000000FF) & *a++;
//将校验和保存在buffer[8~15]
a = Buf + 8;
*( (unsigned int *)a ) = checksum;
//拷贝buffer中的内容到目的bin
fp = fopen(argv[2], "wb");
if (fp == NULL)
{
printf("destination file open error\n");
free(Buf);
return -1;
}
//将16k的buffer拷贝到目的bin中
a = Buf;
nbytes = fwrite( a, 1, BufLen, fp);
if ( nbytes != BufLen )
{
printf("destination file write error\n");
free(Buf);
fclose(fp);
return -1;
}
free(Buf);
fclose(fp);
return 0;
}