Main函数的主要流程:
硬件初始化——》从Dataflash中加载uboot——》返回指定的地址JUMP_ADDR
本文主要分析: 硬件初始化hw_init()
#include "include/part.h"
#include "include/main.h"
#include "include/debug.h"
#include "include/dataflash.h"
#include "include/nandflash.h"
#include "include/norflash.h"
/*------------------------------------------------------------------------------*/
/* Function Name : main */
/* Object : Main function */
/* Input Parameters : none */
/* Output Parameters : True */
/*------------------------------------------------------------------------------*/
int main(void)
{
/* ================== 1st step: HardwareInitialization ================= */
/* Performs the hardware initialization*/
#ifdef CFG_HW_INIT
hw_init();硬件初始化执行(WDT.PLLA.MCK.PLLB.CP15.PIO.EBI.MATRIX.SDRAM)
#endif
/* ==================== 2nd step: Load frommedia ==================== */
/* Load from Dataflash in RAM */
#ifdef CFG_DATAFLASH //Dataflash加载到SDRAM中
load_df(AT91C_SPI_PCS_DATAFLASH, IMG_ADDRESS,IMG_SIZE, JUMP_ADDR);
#endif
/*Load from Nandflash in RAM */
#ifdef CFG_NANDFLASH //不执行暂不分析,主要是从不同的flash中加载UBOOT到SDRAM
load_nandflash(IMG_ADDRESS,IMG_SIZE, JUMP_ADDR);
#endif
/*Load from Norflash in RAM */
#ifdef CFG_NORFLASH//不执行暂不分析,主要是从不同的flash中加载UBOOT到SDRAM
load_norflash(IMG_ADDRESS,IMG_SIZE, JUMP_ADDR);
#endif
/* ==================== 3rd step: Process the Image =================== */
/* Uncompress the image */
#ifdef GUNZIP //解压缩映像文件,不需要,不执行
decompress_image((void*)IMG_ADDRESS, (void *)JUMP_ADDR, IMG_SIZE); /*NOT IMPLEMENTED YET */
#endif /* GUNZIP */
/* ==================== 4th step: Start theapplication =================== */
/* Set linux arguments *///不执行
#ifdef LINUX_ARG//设置启动参数,不需要,因为bootstrap只加载uboot到SDRAM中
linux_arg(LINUX_ARG); /* NOT IMPLEMENTED YET */
#endif /* LINUX_ARG */
/*Jump to the Image Address */
returnJUMP_ADDR;//0x23F00000 /* Final Jump Address */可以自行修改
}
通过对main函数的分析可知启示,在主函数中只有两函数要执行,特定的开发板,那就是:
hw_init()
硬件初始化执行
load_df(AT91C_SPI_PCS_DATAFLASH, IMG_ADDRESS,IMG_SIZE, JUMP_ADDR);
Dataflash加载到SDRAM中
对于从不同的flash中加载主要体现在,硬件是否支持哪种启动方式。
Load from Dataflash inRAM
Load from Nandflash inRAM
Load from Norflash inRAM 需要有针对性的添加修改,来适应自己的开发板
本次移植使用的是AT的官方demo板子画的,所有只移植从Dataflash加载uboot即可。
以下具体分析这两函数的具体实现:
一硬件初始化void hw_init(void)
/*----------------------------------------------------------------------------*/
/* \fn hw_init */
/* \brief This functionperforms very low level HW initialization */
/* This function isinvoked as soon as possible during the c_startup */
/* The bss segment mustbe initialized */
/*----------------------------------------------------------------------------*/
void hw_init(void)
{
unsigned int cp15;
/* Configure PIOs */
const struct pio_desc hw_pio[] = {
#ifdef CFG_DEBUG
{"RXD", AT91C_PIN_PB(14), 0, PIO_DEFAULT, PIO_PERIPH_A},
{"TXD", AT91C_PIN_PB(15), 0, PIO_DEFAULT, PIO_PERIPH_A},
#endif
{(char *) 0, 0, 0, PIO_DEFAULT, PIO_PERIPH_A},
};//此结构体初始化为空,调试阶段有使用价值
/* Disable watchdog */
writel(AT91C_WDTC_WDDIS,AT91C_BASE_WDTC + WDTC_WDMR);
//关闭看门狗(0xFFFFFD40+4)=(0x1 << 15)
/* At this stage the main oscillator is supposed to be enabled
* PCK = MCK = MOSC *///在这个阶段,主振荡器启用PCK = MCK = MOSC
/* Configure PLLA = MOSC * (PLL_MULA + 1) / PLL_DIVA */
pmc_cfg_plla(PLLA_SETTINGS, PLL_LOCK_TIMEOUT);//0x2060BF09
/* PCK = PLLA = 2 * MCK */
pmc_cfg_mck(MCKR_SETTINGS, PLL_LOCK_TIMEOUT);
/* Switch MCK onPLLA output */
pmc_cfg_mck(MCKR_CSS_SETTINGS, PLL_LOCK_TIMEOUT);
/* Configure PLLB */
pmc_cfg_pllb(PLLB_SETTINGS, PLL_LOCK_TIMEOUT);
/* Configure CP15 */
cp15 = get_cp15();//将协处理器P15的寄存器中数据传送到ARM处理器寄存器r0中。
cp15 |= I_CACHE;//set bit 12 (I) I-Cache
set_cp15(cp15);//设置CP15中的寄存器值,禁止指令Cache
/* Configure the PIO controller */
pio_setup(hw_pio);//此函数直接跳出,因为hw_pio结构体的第一个元素是0,调试有用
/* Configure the EBI Slave Slot Cycle to 64 *///简单就是给某个寄存器写入一个值
writel( (readl((AT91C_BASE_MATRIX + MATRIX_SCFG3)) & ~0xFF)| 0x40, (AT91C_BASE_MATRIX+ MATRIX_SCFG3));
//先读出(MATRIX)Base Address+Slave Configuration Register 3 (ebi)寄存器的值,同时将低
//8位清零,再和0x40相与,即置位第7位 01000000 ,
// writel(X1XXXXXXX, 刚刚读出的那个寄存器0xFFFFEE00+0X4C));
//将0XFFFFEE4C寄存器的第7位置1,具体什么功能,查数据手册????????????
#ifdef CFG_DEBUG //调试阶段使用,暂不分析
/* Enable Debugmessages on the DBGU */
dbg_init(BAUDRATE(MASTER_CLOCK, 115200));
dbg_print("Start AT91Bootstrap...\n\r");
#endif /* CFG_DEBUG */
#ifdef CFG_SDRAM
/* Initializethe matrix *///简单就是给某个寄存器写入一个值
writel(readl(AT91C_BASE_CCFG+ CCFG_EBICSA) | AT91C_EBI_CS1A_SDRAMC, AT91C_BASE_CCFG + CCFG_EBICSA);
//先读出(CCFG)Base Address+ EBI Chip Select Assignement Register寄存器的值
//再和(0x1<< 1) // (CCFG) Chip Select 1 is assigned to the SDRAM Controller.相或,即置位第//2位 0000 0010 ,
// writel(XXXXXXX1X, 刚刚读出的那个寄存器0xFFFFEF10+0X0C));
//将0XFFFFEF1C寄存器的第2位置1,具体什么功能,查数据手册????????????
/* Configure SDRAM Controller */
sdram_init( AT91C_SDRAMC_NC_9 |
AT91C_SDRAMC_NR_13 |
AT91C_SDRAMC_CAS_2 |
AT91C_SDRAMC_NB_4_BANKS |
AT91C_SDRAMC_DBW_32_BITS |
AT91C_SDRAMC_TWR_2 |
AT91C_SDRAMC_TRC_7 |
AT91C_SDRAMC_TRP_2 |
AT91C_SDRAMC_TRCD_2 |
AT91C_SDRAMC_TRAS_5 |
AT91C_SDRAMC_TXSR_8, /* Control Register*/
(MASTER_CLOCK *7)/1000000, /* Refresh TimerRegister */
AT91C_SDRAMC_MD_SDRAM); /* SDRAM (no low power) */
#endif /* CFG_SDRAM */
}
两个重要宏定义
#define writel(value,address) \
(*(volatile unsigned int *)(address)) = (value) //向寄存器写入某个值
#define readl(address) \
(*(volatile unsigned int *)(address)) //从寄存器中读取
重点分析以下几个子函数:
前3个是关于PLL的配置,最后一个是SDRAM初始化子函数。
/* Write PMC register */
static inline voidwrite_pmc(unsigned int offset, const unsigned int value)
{
writel(value, offset + AT91C_BASE_PMC);// (0xFFFFFC00) // (PMC) Base Address
}
/* Read PMC registers */
static inline unsignedint read_pmc(unsigned int offset)
{
return readl(offset + AT91C_BASE_PMC);// (0xFFFFFC00) // (PMC) Base Address
}
1//*----------------------------------------------------------------------------
//* \fn pmc_cfg_plla
//* \brief Configurethe pll frequency to the corresponding value.
//*----------------------------------------------------------------------------*/
intpmc_cfg_plla(unsigned int pmc_pllar, unsigned int timeout)
{
write_pmc((unsigned int)PMC_PLLAR, pmc_pllar);
// pmc_pllar=0x2060BF09写入到偏移(40)PLL A Register的寄存器中
while ( (timeout--) && !(read_pmc(PMC_SR) & AT91C_PMC_LOCKA) );//延时等待
//若延时未到,则只有在PMC Status Register的(PMC) PLL A Status=0x02状态位是0x2跳出
//即//延时等待PLLA 锁定频率
return (timeout) ? 0 : (-1);
}
2//*----------------------------------------------------------------------------
//* \fn pmc_cfg_mck
//* \brief Configurethe main oscillator to the corresponding value.
//*----------------------------------------------------------------------------*/
intpmc_cfg_mck(unsigned int pmc_mckr, unsigned int timeout)
{
write_pmc(PMC_MCKR, pmc_mckr);
// pmc_mckr写入到偏移(48) Master ClockRegister的寄存器中
while ( (timeout--) && !(read_pmc(PMC_SR) & AT91C_PMC_MCKRDY) );
//延时等待 Master Clock状态准备就绪
return (timeout) ? 0 : (-1);
}
3
intpmc_cfg_pllb(unsigned int pmc_pllbr, unsigned int timeout)
{
write_pmc(PMC_PLLBR, pmc_pllbr);
// pmc_pllbr=0x10483F0E写入到偏移(44) // PLL B Register的寄存器中
while ( (timeout--) && !(read_pmc(PMC_SR) & AT91C_PMC_LOCKB) );
//延时等待PLLB 锁定频率
return (timeout) ? 0 : (-1);
}
4//*----------------------------------------------------------------------------
//* \fn sdram_init
//* \brief Initializethe SDRAM Controller
//*----------------------------------------------------------------------------
关于SDRAM初始化的重要子函数
/* Write SDRAMCregister */
static inline voidwrite_sdramc(unsigned int offset, const unsigned int value)
{
writel(value, offset + AT91C_BASE_SDRAMC);// 0xFFFFEA00(SDRAMC) Base Address
}
/* Read SDRAMCregisters */
static inline unsignedint read_sdramc(unsigned int offset)
{
return readl(offset + AT91C_BASE_SDRAMC); // 0xFFFFEA00(SDRAMC) Base Address
}
int sdram_init(unsignedint sdramc_cr, unsigned int sdramc_tr, unsigned char low_power)
{
volatile unsigned int i;
/* Performs the hardware initialization */
sdramc_hw_init();//初始化IO,主要是写两寄存器
/* CFG Control Register */
write_sdramc(SDRAMC_CR, sdramc_cr);//主要是设置SDRAM的一些配置参数
//将sdramc_cr的值写入到地址偏移SDRAMC_CR的寄存器中
/* Set MDR Register */
write_sdramc(SDRAMC_MDR, (low_power &0x01)); //(low_power=0 )& 0x01=0
for (i =0; i< 1000;i++);//延时一段时间,到底是多少时间需要好好弄清楚了呀??????
//修改模式寄存器的值,改变工作模式0x1
write_sdramc(SDRAMC_MR, AT91C_SDRAMC_MODE_NOP_CMD); // Set NOP
// (SDRAMC) Issue a NOP Command at every accesswrite_sdramc(SDRAMC_MR,1 );
writel(0x00000000, AT91C_SDRAM);//Perform NOP AT91C_SDRAM=0x20000000
//修改模式寄存器的值,改变工作模式0x2
write_sdramc(SDRAMC_MR,AT91C_SDRAMC_MODE_PRCGALL_CMD);
// Set PRCHG AL
writel(0x00000000, AT91C_SDRAM); // Perform PRCHG AT91C_SDRAM=0x20000000
for (i =0; i< 10000;i++);//延时多少时间??????
//修改模式寄存器的值,改变工作模式0x4。以下代码实现每次设置工作模式为0x4,然后
//在地址0x20000004 写 0x0000 0001
//在地址0x20000008 写 0x0000 0002 依次写直到
//在地址0x20000020 写 0x0000 0008
write_sdramc(SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 1st CBR
writel(0x00000001, AT91C_SDRAM+4); // Perform CBR
write_sdramc(SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 2 CBR
writel(0x00000002, AT91C_SDRAM+8); // Perform CBR
write_sdramc(SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 3 CBR
writel(0x00000003, AT91C_SDRAM+0xc); // Perform CBR
write_sdramc(SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 4 CBR
writel(0x00000004, AT91C_SDRAM+0x10); // Perform CBR
write_sdramc(SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 5 CBR
writel(0x00000005, AT91C_SDRAM+0x14); // Perform CBR
write_sdramc(SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 6 CBR
writel(0x00000006, AT91C_SDRAM+0x18); // Perform CBR
write_sdramc(SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 7 CBR
writel(0x00000007, AT91C_SDRAM+0x1C); //Perform CBR
write_sdramc(SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 8 CBR
writel(0x00000008, AT91C_SDRAM+0x20); //Perform CBR
//修改模式寄存器的值,改变工作模式0x3,
//在地址0x20000024 写 0xcafe dede
write_sdramc(SDRAMC_MR, AT91C_SDRAMC_MODE_LMR_CMD);
//SetLMR operation
writel(0xcafedede, AT91C_SDRAM+0x24); //Perform LMR burst=1, lat=2
//修改刷新时间寄存器的值(MASTER_CLOCK * 7)/1000000, 寄存器要填写的值怎么计算?
//MASTER_CLOCK (198656000/2) 具体这个值是怎么来的
write_sdramc(SDRAMC_TR, sdramc_tr); // Set Refresh Timer
//修改模式寄存器的值,改变工作模式0x0,
write_sdramc(SDRAMC_MR, AT91C_SDRAMC_MODE_NORMAL_CMD);
// Set Normalmode
writel(0x00000000, AT91C_SDRAM); // Perform Normal mode
return 0;
}