ZYNQ FSBL源码分析
FSBL 是ZYNQ的bootloader虽然不是第一个启动的,但属于用户可以更改的启动程序,因此对源码分析是非常有必要的(在FSBL之前有bootRom,这个已经固化)
zynq在运行完芯片内固化的bootRom之后运行的是FSBL程序(first stage boot loader),uboot由FSBL调起,官方说法中将uboot叫做SSBL(second stage boot loader).
以7000系列为例
代码为自动生成的main.c源码(主要看注释,理解下流程)
int main(void)
{
u32 BootModeRegister = 0; //SSBL 启动方式,后面会读取或强制修改,
u32 HandoffAddress = 0;
u32 Status = XST_SUCCESS;
/*
* PCW initialization for MIO,PLL,CLK and DDR
* PS端初始化,包含MIO PLL CLK DDR
*/
Status = ps7_init();
if (Status != FSBL_PS7_INIT_SUCCESS) {
/////如果初始化失败,则打印
fsbl_printf(DEBUG_GENERAL,"PS7_INIT_FAIL : %s\r\n",
getPS7MessageInfo(Status));
OutputStatus(PS7_INIT_FAIL);
/*
* Calling FsblHookFallback instead of Fallback
* since, devcfg driver is not yet initialized
* 异常
*/
FsblHookFallback();
}
/*
* Unlock SLCR for SLCR register write
* SLCR 解锁,开放写权限;
* SLCR = System Level Control Registers,也就是说系统级别寄存器
*/
SlcrUnlock();
/* If Performance measurement is required
* then read the Global Timer value , Please note that the
* time taken for mio, clock and ddr initialisation
* done in the ps7_init function is not accounted in the FSBL
* 如果需要统计FSBL加载的时间,那么需要先记录当前时间
*/
#ifdef FSBL_PERF
XTime tCur = 0;
FsblGetGlobalTime(&tCur);
#endif
/*
* Flush the Caches
* 刷新缓存
*/
Xil_DCacheFlush();
/*
* Disable Data Cache
* 禁用数据缓存
*/
Xil_DCacheDisable();
/*
* Register the Exception handlers
* 注册异常处理
*/
RegisterHandlers();
/*
* Print the FSBL Banner
* 打印FSBL 信息,包括编译时间
*/
fsbl_printf(DEBUG_GENERAL,"\n\rXilinx First Stage Boot Loader \n\r");
fsbl_printf(DEBUG_GENERAL,"Release %d.%d %s-%s\r\n",
SDK_RELEASE_YEAR, SDK_RELEASE_QUARTER,
__DATE__,__TIME__);
#ifdef XPAR_PS7_DDR_0_S_AXI_BASEADDR
/*
* DDR Read/write test
* DDR 读写测试
*/
Status = DDRInitCheck();
if (Status == XST_FAILURE) {
fsbl_printf(DEBUG_GENERAL,"DDR_INIT_FAIL \r\n");
/* Error Handling here */
OutputStatus(DDR_INIT_FAIL);
/*
* Calling FsblHookFallback instead of Fallback
* since, devcfg driver is not yet initialized
*/
FsblHookFallback();
}
/*
* PCAP initialization PCAP初始化,此处的pcap不是wireshark的pcap 包;
* PCAP 全称是Processor Configuration Access Port, 即处理器配置接口,可以理解 PCAP 代表着硬件,那么这座桥的另一边 PS AXI 接口即代表软件。是为一座沟通软件和硬件的桥梁。
*
*/
Status = InitPcap();
if (Status == XST_FAILURE) {
fsbl_printf(DEBUG_GENERAL,"PCAP_INIT_FAIL \n\r");
OutputStatus(PCAP_INIT_FAIL);
/*
* Calling FsblHookFallback instead of Fallback
* since, devcfg driver is not yet initialized
*/
FsblHookFallback();
}
fsbl_printf(DEBUG_INFO,"Devcfg driver initialized \r\n");
/*
* Get the Silicon Version
* 获取芯片版本
*/
GetSiliconVersion();
#ifdef XPAR_XWDTPS_0_BASEADDR
/*
* Check if WDT Reset has occurred or not
* WDT=看门狗,重置
*/
CheckWDTReset();
/*
* Initialize the Watchdog Timer so that it is ready to use
* 初始化看门狗
*/
Status = InitWatchDog();
if (Status == XST_FAILURE) {
fsbl_printf(DEBUG_GENERAL,"WATCHDOG_INIT_FAIL \n\r");
OutputStatus(WDT_INIT_FAIL);
FsblFallback();
}
fsbl_printf(DEBUG_INFO,"Watchdog driver initialized \r\n");
#endif
/*
* Get PCAP controller settings
* 获取
*/
PcapCtrlRegVal = XDcfg_GetControlRegister(DcfgInstPtr);
/*
* Check for AES source key
* AES加密,检查AES秘钥
*/
if (PcapCtrlRegVal & XDCFG_CTRL_PCFG_AES_FUSE_MASK) {
/*
* For E-Fuse AES encryption Watch dog Timer disabled and
* User not allowed to do system reset
*/
#ifdef XPAR_XWDTPS_0_BASEADDR
fsbl_printf(DEBUG_INFO,"Watchdog Timer Disabled\r\n");
XWdtPs_Stop(&Watchdog);
#endif
fsbl_printf(DEBUG_INFO,"User not allowed to do "
"any system resets\r\n");
}
/*
* Store FSBL run state in Reboot Status Register
* 设置FSBL run 状态
*/
MarkFSBLIn();
/*
* Read bootmode register
* 读取 加载方式项,默认是根据外部的引脚配置
*/
BootModeRegister = Xil_In32(BOOT_MODE_REG); ///寄存器地址
BootModeRegister &= BOOT_MODES_MASK;
//BootModeRegister = JTAG_MODE; ///如果有需要,则强制改成某种方式
/*
* QSPI BOOT MODE
* QSPI 加载,从QSPI接口;以下流程都差不多,选择某个外部,然后先初始化,再赋值Access到MoveImage接口。
*/
#ifdef XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR
#ifdef MMC_SUPPORT
/*
* To support MMC boot
* QSPI boot mode detection ignored
*/
if (BootModeRegister == QSPI_MODE) {
BootModeRegister = MMC_MODE;
}
#endif
if (BootModeRegister == QSPI_MODE) {
fsbl_printf(DEBUG_GENERAL,"Boot mode is QSPI\n\r");
InitQspi();
MoveImage = QspiAccess;
fsbl_printf(DEBUG_INFO,"QSPI Init Done \r\n");
} else
#endif
/*
* NAND BOOT MODE
*/
#ifdef XPAR_PS7_NAND_0_BASEADDR
if (BootModeRegister == NAND_FLASH_MODE) {
/*
* Boot ROM always initialize the nand at lower speed
* This is the chance to put it to an optimum speed for your nand
* device
*/
fsbl_printf(DEBUG_GENERAL,"Boot mode is NAND\n");
Status = InitNand();
if (Status != XST_SUCCESS) {
fsbl_printf(DEBUG_GENERAL,"NAND_INIT_FAIL \r\n");
/*
* Error Handling here
*/
OutputStatus(NAND_INIT_FAIL);
FsblFallback();
}
MoveImage = NandAccess;
fsbl_printf(DEBUG_INFO,"NAND Init Done \r\n");
} else
#endif
/*
* NOR BOOT MODE
*/
if (BootModeRegister == NOR_FLASH_MODE) {
fsbl_printf(DEBUG_GENERAL,"Boot mode is NOR\n\r");
/*
* Boot ROM always initialize the nor at lower speed
* This is the chance to put it to an optimum speed for your nor
* device
*/
InitNor();
fsbl_printf(DEBUG_INFO,"NOR Init Done \r\n");
MoveImage = NorAccess;
} else
/*
* SD BOOT MODE
*/
#if defined(XPAR_PS7_SD_0_S_AXI_BASEADDR) || defined(XPAR_XSDPS_0_BASEADDR)
if (BootModeRegister == SD_MODE) {
fsbl_printf(DEBUG_GENERAL,"Boot mode is SD\r\n");
/*
* SD initialization returns file open error or success
*/
Status = InitSD("BOOT.BIN");
if (Status != XST_SUCCESS) {
fsbl_printf(DEBUG_GENERAL,"SD_INIT_FAIL\r\n");
OutputStatus(SD_INIT_FAIL);
FsblFallback();
}
MoveImage = SDAccess;
fsbl_printf(DEBUG_INFO,"SD Init Done \r\n");
} else
if (BootModeRegister == MMC_MODE) {
fsbl_printf(DEBUG_GENERAL,"Booting Device is MMC\r\n");
/*
* MMC initialization returns file open error or success
*/
Status = InitSD("BOOT.BIN");
if (Status != XST_SUCCESS) {
fsbl_printf(DEBUG_GENERAL,"MMC_INIT_FAIL\r\n");
OutputStatus(SD_INIT_FAIL);
FsblFallback();
}
MoveImage = SDAccess;
fsbl_printf(DEBUG_INFO,"MMC Init Done \r\n");
} else
#endif
/*
* JTAG BOOT MODE
*/
if (BootModeRegister == JTAG_MODE) {
fsbl_printf(DEBUG_GENERAL,"Boot mode is JTAG\r\n");
/*
* Stop the Watchdog before JTAG handoff
*/
#ifdef XPAR_XWDTPS_0_BASEADDR
XWdtPs_Stop(&Watchdog);
#endif
/*
* Clear our mark in reboot status register
*/
ClearFSBLIn();
/*
* SLCR lock
*/
SlcrLock();
FsblHandoffJtagExit();
} else {
fsbl_printf(DEBUG_GENERAL,"ILLEGAL_BOOT_MODE \r\n");
OutputStatus(ILLEGAL_BOOT_MODE);
/*
* fallback starts, no return
*/
FsblFallback();
}
fsbl_printf(DEBUG_INFO,"Flash Base Address: 0x%08lx\r\n", FlashReadBaseAddress);
/*
* Check for valid flash address
*/
if ((FlashReadBaseAddress != XPS_QSPI_LINEAR_BASEADDR) &&
(FlashReadBaseAddress != XPS_NAND_BASEADDR) &&
(FlashReadBaseAddress != XPS_NOR_BASEADDR) &&
(FlashReadBaseAddress != XPS_SDIO0_BASEADDR)) {
fsbl_printf(DEBUG_GENERAL,"INVALID_FLASH_ADDRESS \r\n");
OutputStatus(INVALID_FLASH_ADDRESS);
FsblFallback();
}
/*
* NOR and QSPI (parallel) are linear boot devices
*/
if ((FlashReadBaseAddress == XPS_NOR_BASEADDR)) {
fsbl_printf(DEBUG_INFO, "Linear Boot Device\r\n");
LinearBootDeviceFlag = 1;
}
#ifdef XPAR_XWDTPS_0_BASEADDR
/*
* Prevent WDT reset
*/
XWdtPs_RestartWdt(&Watchdog);
#endif
/*
* This used only in case of E-Fuse encryption
* For image search
*/
SystemInitFlag = 1;
/*
* Load boot image
*/
HandoffAddress = LoadBootImage();
fsbl_printf(DEBUG_INFO,"Handoff Address: 0x%08lx\r\n",HandoffAddress);
/*
* For Performance measurement
*/
#ifdef FSBL_PERF
XTime tEnd = 0;
fsbl_printf(DEBUG_GENERAL,"Total Execution time is ");
FsblMeasurePerfTime(tCur,tEnd);
#endif
/*
* FSBL handoff to valid handoff address or
* exit in JTAG
*/
FsblHandoff(HandoffAddress);
#else
OutputStatus(NO_DDR);
FsblFallback();
#endif
return Status;
}
满屏源代码,一把辐射泪,都云编程痴,谁解其中味!