0 概述
本文主要分析PXA255开发板带的linux系统中的bootloader。
1 BootLoader流程框架
链接文件:描述了bootloader链接的规则。
ENTRY(_start)
SECTIONS
{
. = ALIGN(4);
.text 0xA4000000 - 0x80000:
{
_ld_text_start = .;
*(.text)
*(.got)
*(.got.pld)
*(.rodata)
_ld_text_end = .;
}
_ld_text_size = SIZEOF(.text);
_ld_stack_address = _ld_text_start + 0x80000;
}
这里定义了入口:_start
定义了各个段的属性:
.text 0xA4000000 - 0x80000:
入口点:
_start:
b reset
跳转到reset处执行
reset:
// Mask All interrupt 把ICMR/ ICLR清零
ldr r12, =INTERRUPT_CONTROL_BASE
ldr r0, =0x00000000
str r0, [r12, #ICMR]
str r0, [r12, #ICLR]
// Initialize GPIO 初始化GPIO
bl gpio_init
// Initialize SDRAM 初始化SDRAM
bl init_sdram
// Copy Bootloader from Flash to SDRAM 将bootloader代码搬移到SDRAM
bl copy_to_ram
// Loading kernel image ?
ldr r4, =KERNEL_SRAM_BASE
ldr r5, =KERNEL_DRAM_BASE
ldr r6, =KERNEL_MAX_SIZE
add r6, r6, r4
repeat:
ldmia r4!, {r0-r3, r7-r10}
stmia r5!, {r0-r3, r7-r10}
cmp r4, r6
blt repeat
ldr sp, =_ld_stack_address
// Jump to c_main bootloader的c代码的主程序
ldr r0, =c_main
mov pc, r0
die: //死循环,无其他意义,起保护作用。
b die
1.3初始化GPIO
这段代码很简单,就是把GPIO的初始值设置好。这里需要做的一个工作是结合具体的资料分析其初始值的含义。
gpio_init:
ldr r12, =GPIO_BASE
ldr r0, =GAFR0L_VALUE
str r0, [r12, #GAFR0_L]
ldr r0, =GAFR0U_VALUE
str r0, [r12, #GAFR0_U]
ldr r0, =GAFR1L_VALUE
str r0, [r12, #GAFR1_L]
ldr r0, =GAFR1U_VALUE
str r0, [r12, #GAFR1_U]
ldr r0, =GAFR2L_VALUE
str r0, [r12, #GAFR2_L]
ldr r0, =GAFR2U_VALUE
str r0, [r12, #GAFR2_U]
ldr r0, =GPSR0_VALUE
str r0, [r12, #GPSR0]
ldr r0, =GPSR1_VALUE
str r0, [r12, #GPSR1]
ldr r0, =GPSR2_VALUE
str r0, [r12, #GPSR2]
ldr r0, =GPCR0_VALUE
str r0, [r12, #GPCR0]
ldr r0, =GPCR1_VALUE
str r0, [r12, #GPCR1]
ldr r0, =GPCR2_VALUE
str r0, [r12, #GPCR2]
ldr r0, =GPDR0_VALUE
str r0, [r12, #GPDR0]
ldr r0, =GPDR1_VALUE
str r0, [r12, #GPDR1]
ldr r0, =GPDR2_VALUE
str r0, [r12, #GPDR2]
// Clear the peripheral control register bits
ldr r1, =PSSR
ldr r2, =(PSSR_RDH | PSSR_PH)
str r2, [r1]
mov pc, lr
1.4 初始化SDRAM
关于处理器复位后内存相关的初始化过程在开发手册上有详细说明。
Software performs the following procedures when the processor comes out of a reset:
1. After hardware reset, complete a power-on wait period of 200 µs, which allows the internal clocks that generate SDCLK to stabilize. Enable MDREFR:K0RUN and E0PIN for Synchronous Static memory. When MDREFR is written, a refresh interval value (MDREFR:DRI) must also be written. The following writes are allowed:
a. Write MSC0, MSC1, MSC2
b. Write MECR, MCMEM0, MCMEM1, MCATT0, MCATT1, MCIO0, MCIO1
c. Write MDREFR:K0RUN and MDREFR:E0PIN. Configure MDREFR:K0DB2. Retain the current values of MDREFR:APD and MDREFR:SLFRSH. MDREFR:DRI must contain a valid value. Deassert MDREFR:KxFREE.
2. In systems that contain Synchronous Static memory, write to the SXCNFG to configure all appropriate bits, including the enable bits. Software must perform a sequence that involves a subsequent write to SXCNFG to change the RAS latencies. While any SMROM banks are being configured, the SDRAM banks must be disabled and MDREFR:APD must be deasserted (auto-power-down disabled).
a. Write SXCNFG (with enable bits asserted).
b. Write to SXMRS to trigger an MRS command to all enabled banks of synchronous static memory.
c. SXLCR must only be written when it is required by the SDRAM-like synchronous flash device for command encoding.
3. In systems that contain SDRAM, transition the SDRAM controller through the following state sequence:
a. self-refresh and clock-stop
b. self-refresh
c. power-down
d. PWRDNX
e. NOP
4. The SDRAM clock run and enable bits (MDREFR:K1RUN, K2RUN, and E1PIN), described in Section 6.5.3. MDREFR:SLFRSH must not be asserted.
a. Write MDREFR:K1RUN, K2RUN (self-refresh and clock-stop -> self-refresh). Configure MDREFR:K1DB2,K2DB2.
b. Write MDREFR:SLFRSH (self-refresh -> power-down).
c. Write MDREFR:E1PIN (power-down -> PWRDNX).
d. a write is not required for this state transition (PWRDNX -> NOP).
e. Configure, but do not enable, each SDRAM partition pair.
f. Write MDCNFG (with enable bits deasserted), MDCNFG:DE3:2,1:0 set to ‘0’.
5. For systems that contain SDRAM, wait a specified NOP power-up waiting period required by the SDRAMs to ensure the SDRAMs receive a stable clock with a NOP condition
6. Ensure the Data Cache bit (DCACHE) is disabled. If this bit is enabled, the refreshes triggered by the next step may not pass through to the Memory Controller properly.
7. On a hardware reset in systems that contain SDRAM, trigger the specified number (typically eight) of refresh cycles by attempting non-burst read or write accesses to any disabled SDRAM bank. Each such access causes a simultaneous CBR refresh cycles for all four banks, which causes a pass through the CBR state and back to NOP. On the first pass, the PALL state occurs before the CBR state.
8. Re-enable the DCACHE bit if it is disabled.
9. In systems that contain SDRAM, enable SDRAM partitions by setting MDCNFG:DE3:2,DE1:0.
10. In systems containing SDRAM, write the MDMRS register to trigger an MRS command to all enabled banks of SDRAM. For each SDRAM partition pair that has one or both partitions enabled, this forces a pass through the MRS state and back to NOP. The CAS latency must be the only variable option and is derived from the value programmed in the MDCNFG:MDTC0,2 fields. The burst type is programmed to sequential and the length is set to four.
寄存器说明:
CKEN:
0x00000040:CKEN6 =1 (FFUART Unit Clock Enable)
CCCR:
0x00000161( |010|11|00001):
N: 010 ( Turbo mode Freq. = Run mode frequency * N)
M:11
L: 00001
(Crystal Frequency to Memory Frequency Multiplier)
PXA255晶震频率: 内存频率:
运行频率: Turbo模式频率:
OSCC:
0x00000002:32.768 KHz oscillator is enabled.
OSCR:The current value of the OS timer counter.
先给OSCR赋0,然后等到该值增长到 300。
MSC0/1/2:
0x7ff812B8:
RBUFF1=0 “Slower device” RRR1=7 “ROM/SRAM recovery time.” ?
RDN1 = F “ROM delay next access”
RDF1 = F “ROM delay first access. ”
RBW1 =1 “ROM bus width ” 0: 32bit 1: 16bit
RT1 = 000 “ROM type” 000 - Nonburst ROM or Flash Memory
RBUFF0=0 “Slower device” RRR0=1 “ROM/SRAM recovery time.” ?
RDN0 = 2 “ROM delay next access”
RDF0 = B “ROM delay first access. ”
RBW0 =1 “ROM bus width ” 0: 32bit 1: 16bit
RT0 = 000 “ROM type” 000 - Nonburst ROM or Flash Memory
0x7ff87FF0: 就是RBW0=0 为32bit。
0x12BC5554:
RBUFF3=0 “Slower device” RRR3=1 “ROM/SRAM recovery time.” ?
RDN3 = 2 “ROM delay next access”
RDF3 = b “ROM delay first access. ”
RBW3 =1 “ROM bus width ” 0: 32bit 1: 16bit
RT3 = 100 “ROM type” 100 - Variable Latency I/O (VLIO)
RBUFF2=0 “Slower device” RRR2=5 “ROM/SRAM recovery time.” ?
RDN0 = 5 “ROM delay next access”
RDF0 = 5 “ROM delay first access. ”
RBW0 =0 “ROM bus width ” 0: 32bit 1: 16bit
RT0 = 100 “ROM type” 100 - Variable Latency I/O (VLIO)
0x7FF87FF1: 同前,就是RT4 = 001 001 - SRAM
MECR: (Expansion Memory Configuration Register)
0x00000000: 表示 0 – No card inserted
0x00000002:表示有卡,且一个socket。
MCMEM0/1, MCATT0/1, MCIO0/1:(contain control bits for configuring the timing of the 16-bit PC Card/Compact Flash interface.)
新开发板上没有,不考虑。
MDREFR:
0x000BC018:
DRI = 0x018 SDRAM refresh interval, all partitions.
E0PIN = 0 Synchronous Static Memory Clock Enable Pin 0
K0RUN=0 Synchronous Static Memory Clock Run Pin 0
K0DB2=1 Synchronous Static Memory Clock Pin 0 (SDCLK<0>)
SDCLK0 runs at one-half the memory clock frequency.
E1PIN=1 SDRAM Clock Enable Pin 1 (SDCKE1)
K1RUN=1 SDRAM Clock Pin 1 (SDCLK<1>) enable
K1DB2=1 SDCLK1 runs at one-half the MEMCLK frequency
K2RUN=0 SDRAM Clock Pin 2 (SDCLK<2>) disable
K2DB2=1 SDCLK2 runs at one-half the MEMCLK frequency
APD=0
SLFRSH=0
K0/1/2FREERUN =0
MDCNFG:
0x00001AC9:
DE0=1 SDRAM enable for partition 0 DE1=0
DWID0=0 SDRAM data bus width for partition pair 0/1 (0 – 32 bits)
DCAC0=01 Number of Column Address bits for partition pair 0/1 (01 - 9 column address bits)
DRAC0=10 13 row address bits
DNB0 = 1 4 internal SDRAM banks
DTC0=10 tRP = 3 clks, CL = 3, tRCD = 3 clks, tRAS(min) =7 clks, tRC=10 clks
DADDR0=0
DLATCH0=1 Latch return data with return clock
DSA1111_0=1 Use SA1111 Addressing Muxing Mode for pair 0/1.
MDMRS:Mode Register Set Configuration Register
init_sdram:
mov r10, lr
ldr r12, =CLOCK_MANAGER_BASE
ldr r0, =CKEN_VALUE
str r0, [r12, #CKEN]
ldr r0, =OSCC_VALUE
str r0, [r12, #OSCC]
#if 1
//TODO: Issuing a FCS on a B1 seems to make it crash
//shortly after. Figure out why...
ldr r0, =CCCR_VALUE
str r0, [r12, #CCCR]
mov r1, #3
mcr p14, 0, r1, c6, c0, 0
ldr r1, =OSCR
ldr r0, =0
str r0, [r1]
ldr r0, =0x300
wait_for_clock:
ldr r2, [r1]
cmp r0, r2
bne wait_for_clock
#endif
//Step 1 in Intel's code
ldr r12, =MEM_CTL_BASE
ldr r0, =MSC0_VALUE
str r0, [r12, #MSC0]
//Intel's code reads it back to make sure it works...
// ldr r0, [r12, #MSC0]
ldr r0, =MSC1_VALUE
str r0, [r12, #MSC1]
ldr r0, [r12, #MSC1]
ldr r0, =MSC2_VALUE
str r0, [r12, #MSC2]
ldr r0, [r12, #MSC2]
ldr r0, =MECR_VALUE
str r0, [r12, #MECR]
ldr r0, =MCMEM0_VALUE
str r0, [r12, #MCMEM0]
ldr r0, =MCMEM1_VALUE
str r0, [r12, #MCMEM1]
ldr r0, =MCATT0_VALUE
str r0, [r12, #MCATT0]
ldr r0, =MCATT1_VALUE
str r0, [r12, #MCATT1]
ldr r0, =MCIO0_VALUE
str r0, [r12, #MCIO0]
ldr r0, =MCIO1_VALUE
str r0, [r12, #MCIO1]
//according to Intel's comments, we're extracting the DRI
// Loading the MDREFR in a way that will make it configure
// correctly is a multi-step process. Please read Section
// 6.12 of the PXA Developers Manual For more Details
// 1.
// The first step requires that we set K0RUN and E0PIN while
// configuring DRI and clearing KXFREE. All other values
// MUST be left alone
ldr r0, =MDREFR_VALUE
ldr r3, [r12, #MDREFR]
ldr r1, =0xFFF
and r0, r0, r1
// Make the DRI we read from MDREFR what MDREFR_VALUE says it is.
// We also Free KXFREE the free running bits.
bic r3, r3, r1
bic r3, r3, #0x03800000
orr r3, r3, r0
//Write it back
str r3, [r12, #MDREFR]
// 2.
// We don't have Synchronous Static Memory and don't want to
// mess with SXCNFG or the like so we are leaving out this
// step.
// 3.
// We don't bother to do this step as it does not seem to have
// been done previously (actually maybe Self-Refresh Disable should
// be here.)
// 4.
// Here we will setup the SDCLK's but WILL NOT enable them. We
// need to reload MDREFR for this.
ldr r0, =MDREFR_VALUE
ldr r1, =0xF6000 // Mask of SDCLK's settings minus EXPIN
and r0, r0, r1
bic r3, r3, r1
orr r3, r3, r0
str r3, [r12, #MDREFR]
ldr r3, [r12, #MDREFR]
// 4.
// Although I think that this should be at #3 It is here cause this
// is where it was originally. This will turn off Self-Refresh.
bic r3, r3, #0x00400000
str r3, [r12, #MDREFR]
// 5.
// Finally, we Enable the Various SDCLK's and let it run.
// Also, enable the free-running clocks (not mentioned in the manual).
ldr r0, =MDREFR_VALUE
ldr r1, =0x03809000
and r0, r0, r1
orr r3, r3, r0
str r3, [r12, #MDREFR]
nop
nop
//Step 4 in Intel's code
ldr r0, =MDCNFG_VALUE
//disable all sdram banks
bic r0, r0, #0x00000003
bic r0, r0, #0x00030000
//program banks 0/1 for 32 bit bus width
bic r0, r0, #0x00000004
//test with 16 bit bus width
// orr r0, r0, #0x00000004
//write MDCNFG, without enabling SDRAM banks
str r0, [r12, #MDCNFG]
//Step 5 in Intel's code
ldr r0, =OSCR
mov r1, #0
str r1, [r0]
//pause for approx 200 usecs
ldr r4, =0x300
sdram_dly:
ldr r1, [r0]
cmp r4, r1
bgt sdram_dly
//Step 6 in Intel's code
//turn everything off
mov r0, #0x78
mcr p15, 0, r0, c1, c0, 0
//Step 7 in Intel's code
//Access memory that has not been enabled for CBR refresh cycles (8)
ldr r0, =SDRAM_BASE
str r0, [r0]
str r0, [r0]
str r0, [r0]
str r0, [r0]
str r0, [r0]
str r0, [r0]
str r0, [r0]
str r0, [r0]
//Step 8 is blank in Intel's code, though they mention dcache should
//be enabled here if it is desired (we don't care)
//Step 9
ldr r0, [r12, #MDCNFG]
//enable bank 0 (what about bank 1?)
orr r0, r0, #0x00000001
str r0, [r12, #MDCNFG]
//Step 10
//write MDMRS again
ldr r0, =MDMRS_VALUE
str r0, [r12, #MDMRS]
//Step 11
//are we A1_Cotulla?
ldr r0, [r12, #MDREFR]
ldr r11, =0xFFEFFFFF
and r0, r0, r11
str r0, [r12, #MDREFR]
mov pc, r10
把从地址0开始,长度为_ld_text_and_data_size的代码数据copy到_ld_text_start位置中去。
由1.1可以知道,_ld_text_start 应该为:0xA4000000
copy_to_ram:
mov r8, lr
ldr r0, =0
ldr r1, =_ld_text_start
ldr r2, =_ld_text_and_data_size
copy_loop:
ldr r3, [r0]
str r3, [r1]
add r0, r0, #4
add r1, r1, #4
subs r2, r2, #4
bne copy_loop
mov pc, r8
1.6装载内核
把操作系统内核代码从flash空间的0x000C0000位置搬移到内存空间:0xA0008000中去。最大不超过2M左右。
// Loading kernel image
ldr r4, =KERNEL_SRAM_BASE
ldr r5, =KERNEL_DRAM_BASE
ldr r6, =KERNEL_MAX_SIZE
add r6, r6, r4
repeat:
ldmia r4!, {r0-r3, r7-r10}
stmia r5!, {r0-r3, r7-r10}
cmp r4, r6
blt repeat
ldr sp, =_ld_stack_address
主要就是几个初始化函数,然后判断是进入操作系统,还是进而交互界面。
(*((volatile unsigned short *)( 0x16000000)))=~0x4;
// serial and timer init.
SerialInit(status.terminalSpeed);
TimerInit();
EthInit();
1.8 串口初始化
用到的寄存器说明:
GAFR1_L:
0x000A8000:表示AF39,AF40,AF41:0x10 (used for its alternate function 2)
GPDR1:
0x00000380:表示PD39,PD40,PD41都为1。(configured as output.)
FFLCR:
0x00000003:表示字节长度为8,停止位为1,无奇偶检验,
FFFCR:
0x00000007:表示FIFOs are enabled,The receiver FIFO is cleared,The transmitter FIFO is cleared。
FFIER:
0x00000040:表示UUE=1 (the unit is enable)
DLL:在设置DLL的值之前,需要先设置LCR的对应位。设置完DLL的值之后,还需要清除LCR中的对应位。DLL和DLH设置的是分频值。这里取值为8,得到波特率为115200。
LSR:
0x00000040:表示检查TEMT,等该条件为1满足退出。
(All the data in the transmitter has been shifted out)
void SerialInit(ulong baud){
// GP39, GP40, GP41
GAFR1_L |= 0x000A8000;
GPDR1 |= 0x00000380;
// 8-bit, 1 stop, no parity
FFLCR = 0x00000003;
// Reset tx, rx FIFO. clear. FIFO enable
FFFCR = 0x00000007;
// UART Enable Interrupt
FFIER = 0x00000040;
// DLAB set=latch registers, DLAB clear=
FFLCR |= 0x00000080;
// baud rate
FFDLL = baud;
// DLAB clear,
FFLCR &= 0xFFFFFF7F;
// Transmit Shift Register, Transmit Holding Register, FIFO俊
//
while(! FFLSR & 0x00000040 );
return;
}
1.9 以太网初始化
寄存器的详细信息如下:
lineCTL:
0x0000:表示Rx, Tx disable。
ISAINT:
ISADMA:
RXCNF:能使能各种中断
RxCTL:配置什么特征的报文可以接收
RX_OK_ACCEPT:接收正确的报文(长度和CRC)
RX_IA_ACCEPT:接收目标地址匹配的报文。
TxCFG:能使能各种中断
TX_LOST_CRS_ENBL
TX_SQE_ERROR_ENBL
TX_OK_ENBL
TX_LATE_COL_ENBL
TX_JBR_ENBL
TX_ANY_COL_ENBL
TxCMD:包含着最后的传输命令,指明下一个报文如何发送。
0x00C0:TxStart等于
BufCFG:
0x0000:禁止各种buf相关的中断。
BusCTL:
ENABLE_IRQ:使能网卡中断
IO_CHANNEL_READY_ON :
TestCTL:可以用来设置各种测试功能。
0x0000:禁止掉各种测试功能。
TxCommand:指明下一个报文如何传输 (同TXCMD一样,但地址不一样?)
0x00c0:txstart为:
Hash Table:(LAF)
Physical Address Register(IA)
bool EthInit(){
int i;
// 1. check memory mapping (read IOBase Address).
// check value of IO_PPPTR is 0x3000.
// 1~3 bit(011b) is read only. so clear others.
MemSet((char *)0x04000000, 0x0, 2); // Test Code
if ((IO_PPPTR&0x7000) != 0x3000){
printf("Can't access to Memory of CS8900A.\n");
return false;
}
// 2. check CS8900A Chip ID (read packet page 0x0000~0x0003).
if (ReadFromPPR(PP_ChipID)!=0x630E){
printf("\nEthernet Chip is not CS8900A. Are you use CS8900A.\n");
return false;
}
// 3. Rx, Tx disable.
WriteToPPR(PP_LineCTL, 0x0000);
// 4. if needs, set Bus Interface Registers(view CS8900 Manual 4.2 p40).
WriteToPPR(PP_CS8900_ISAINT, INTRQ_HIGH_IMPEDENCE);
WriteToPPR(PP_CS8900_ISADMA, DMRQ_HIGH_IMPEDENCE);
// 5. set Status and Control Registers.
WriteToPPR(PP_RxCFG, RX_OK_ENBL | RX_CRC_ERROR_ENBL);
WriteToPPR(PP_RxCTL, RX_OK_ACCEPT | RX_IA_ACCEPT);
WriteToPPR(PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL);
WriteToPPR(PP_TxCMD, TX_START_ALL_BYTES);
WriteToPPR(PP_BufCFG, 0x0000);
WriteToPPR(PP_BusCTL, ENABLE_IRQ | IO_CHANNEL_READY_ON);
WriteToPPR(PP_TestCTL, 0x0000);
///// Initiate Transmit Registers. ////////////////////////////////////////
WriteToPPR(PP_TxCommand,0x00c0); // start Tx after the entire frame is in the CS8900A.
// 6. set Address Filter Registers.
// set Logical Address Filter.
for (i=0; i<4; i++){
WriteToPPR(PP_LAF+i*2, 0xffff);
}
for (i=0; i<3; i++){
WriteToPPR(PP_IA+i*2, *((ushort *)&(clientEther[i*2])));
}
// 7. Rx, Tx enable.
WriteToPPR(PP_LineCTL, SERIAL_RX_ON | SERIAL_TX_ON);
return true;
} // EthInit.
程序跳转到0xA0008000,并且传递了两个参数:0,200。
参数的含义:0是ARM启动需要的一个值,没有特殊用途。
200是指系统的系统结构。具体类型在ARM的启动代码中有定义。
#define KERNEL_DRAM_BASE (0xA0008000)
theKernel = (void (*)(int, int))KERNEL_DRAM_BASE;
theKernel(0, 200);
2 FLASH操作接口
2.1 擦除块
擦除某一段FLASH地址空间。
bool EraseFlashBlocks(FUNIT *addr, ulong len)
在FLASH中,内核,bootloader,root各自的位置都已经固定。
BOOT:0x00000000 最大:0x00040000
内核: 0x000C0000 最大:0x00200000
root: 0x002C0000 最大: 0x00E00000 / 0x01D00000
擦除具体某一block FLASH.
static bool EraseOneFlashBlock(FUNIT *addr)
2.2 写FLASH
写FLASH之前需要先对该区域进行擦除。
bool WriteToFlashBuffer(void *dest, void *src)
一次写一块大小。
3.1 串口输出
LSR:
TDRQ:Indicates that the UART is ready to accept a new character for transmission.
THR: In FIFO mode, a write to the THR puts data into the end of the FIFO. The data at the front of the FIFO is loaded to the TSR when that register is empty.
void SerialOutputByte(const char c){
while ((FFLSR & 0x00000020) == 0 );
FFTHR = ((ulong)c & 0xFF);
if (c=='\n') SerialOutputByte('\r');
}
3.2 串口输入
LSR寄存器中的DR=1: DATA is ready. Data is available in RBR or the FIFO
RBR: In FIFO mode, the RBR latches the value of the data byte at the front of the FIFO.
int SerialInputByte(char *c){
if((FFLSR & 0x00000001)==0){
return 0;
} else {
(volatile char)*c = FFRBR;
return 1;
}
}

