对FSBL中main.c的分析

u32 BootModeRegister = 0;  //存放启动模式寄存器值
u32 HandoffAddress = 0; //交接代码地址
u32 Status = XST_SUCCESS; //执行状态

PCW initialization for MIO,PLL,CLK and DDR

/*
* PCW initialization for MIO,PLL,CLK and 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();
}

ps7_init()函数位于.sdk文件夹wrapper_hw_platform下。该文件夹下有ps7_init.c、ps7_init.h、ps7_init.tcl、ps7_init.html(该文件展示了ps的详细配置)、wrapper.bit和system.hdf文件。
ps7_init.tcl和ps7_init.c的生成有关(ps7_init.c中的每个函数在ps7_init.tcl中都有一个过程相对应)。

if (si_ver == PCW_SILICON_VERSION_1) {
ps7_mio_init_data = ps7_mio_init_data_1_0;
ps7_pll_init_data = ps7_pll_init_data_1_0;
ps7_clock_init_data = ps7_clock_init_data_1_0;
ps7_ddr_init_data = ps7_ddr_init_data_1_0;
ps7_peripherals_init_data = ps7_peripherals_init_data_1_0;
}

该段代码是ps7_init()中的对不同芯片版本选择不同初始化数据的一段代码。ps7_mio_init_data_1_0是数字的首地址,数组中存放对寄存器操作的记录。

unsigned long ps7_mio_init_data_1_0[] = {
EMIT_WRITE(0XF8000008, 0x0000DF0DU),
...
}

从ps7_init.h中找到数组中该句的定义,即为一条记录:操作 地址 值

#define OPCODE_WRITE      2U
#define EMIT_WRITE(addr,val) ( (OPCODE_WRITE << 4 ) | 2 ) , addr, val

通过ps7_config函数对数据进行解析和配置。

ret = ps7_config (ps7_mio_init_data);
if (ret != PS7_INIT_SUCCESS) return ret;

总体上来说ps7_init对MIO、PLL、Clock、DDR、Peripherals做了初始化工作,即配置相应的寄存器。

fsbl_printf(DEBUG_GENERAL,"PS7_INIT_FAIL : %s\r\n",
getPS7MessageInfo(Status));

定义在fsbl_debug.h中

#define fsbl_printf(type,...) \
if (((type) & fsbl_dbg_current_types)) {xil_printf (__VA_ARGS__); }

调试fsbl的时候可以打开debug,这样串口就有打印信息了。

我们通过追踪OutputStatus函数,其输出状态是通过xil_printf函数实现(xil_printf.c是处于fsbl_bsp\ps7_cortexa9_0\libsrc\standalone_v6_2\src),xil_printf函数调用outbyte函数(fsbl_bsp\ps7_cortexa9_0\libsrc\standalone_v6_2\src),outbyte函数是简单封装了XUartPs_SendByte函数(该函数处于xuartps_hw.c,fsbl_bsp\ps7_cortexa9_0\libsrc\uartps_v3_4\src),而该函数是在其.h文件里面进行宏定义的函数。

#define XUartPs_WriteReg(BaseAddress, RegOffset, RegisterValue) \
Xil_Out32((BaseAddress) + (u32)(RegOffset), (u32)(RegisterValue))

而Xil_Out32是xil_io.h中inline函数,是通过对寄存器写数据完成操作的。
从中可以看出standalone_v6_2是与平台无关的对底层函数的封装,ps7_cortexa9_0\libsrc\uartps_v3_4是和硬件相关函数的实现,而最终实现是使用整合在统一的xil开头的对寄存器操作的文件中。

Unlock SLCR for SLCR register write

#define SlcrUnlock() Xil_Out32(XPS_SYS_CTRL_BASEADDR + 0x08, 0xDF0DDF0D)

Flush the Caches

Disable Data Cache

Register the Exception handlers

通过异常处理名和异常处理函数地址对XExc_VectorTable进行注册,这里相当于异常处理函数全部指向0地址。

Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_UNDEFINED_INT,
(Xil_ExceptionHandler)Undef_Handler,
(void *) 0);

函数处于xil_exception.c中

void Xil_ExceptionRegisterHandler(u32 Exception_id,
Xil_ExceptionHandler Handler,
void *Data)
{
XExc_VectorTable[Exception_id].Handler = Handler;
XExc_VectorTable[Exception_id].Data = Data;
}
XExc_VectorTableEntry XExc_VectorTable[XIL_EXCEPTION_ID_LAST + 1] =
{
{Xil_ExceptionNullHandler, NULL},
{Xil_UndefinedExceptionHandler, NULL},
{Xil_ExceptionNullHandler, NULL},
{Xil_PrefetchAbortHandler, NULL},
{Xil_DataAbortHandler, NULL},
{Xil_ExceptionNullHandler, NULL},
{Xil_ExceptionNullHandler, NULL},
};

DDR Read/write test

Status = DDRInitCheck();向DDR首地址写入数据,然后读出,看是否相同。

CAP initialization

Status = InitPcap();
Initialize the Device Configuration Interface driver.

ConfigPtr = XDcfg_LookupConfig(DCFG_DEVICE_ID);
Status = XDcfg_CfgInitialize(DcfgInstPtr, ConfigPtr,
ConfigPtr->BaseAddr);

Lookup the device configuration based on the unique device ID. The table
contains the configuration info for each device in the system.
@param DeviceId is the unique device ID of the device being looked up.
@return A pointer to the configuration table entry corresponding to the
given device ID, or NULL if no match is found.

XDcfg_Config *XDcfg_LookupConfig(u16 DeviceId)
{
extern XDcfg_Config XDcfg_ConfigTable[];
XDcfg_Config *CfgPtr = NULL;
int Index;
for (Index = 0; Index < XPAR_XDCFG_NUM_INSTANCES; Index++) {
if (XDcfg_ConfigTable[Index].DeviceId == DeviceId) {
CfgPtr = &XDcfg_ConfigTable[Index];
break;
}
}
return (CfgPtr);
}

Copy configuration into instance. Save the base address pointer such that the registers of the block can be accessed and indicate it has not been started yet. Unlock the Device Configuration Interface. Indicate the instance is ready to use, successfully initialized.
XDcfg_CfgInitialize数主要流程:
InstancePtr->Config.DeviceId = ConfigPtr->DeviceId;
InstancePtr->Config.BaseAddr = EffectiveAddress;
InstancePtr->IsStarted = 0;
XDcfg_Unlock(InstancePtr);
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;

Store FSBL run state in Reboot Status Register

Read bootmode register

BootModeRegister = Xil_In32(BOOT_MODE_REG);
BootModeRegister &= BOOT_MODES_MASK;

分析各种启动模式下Init和Load boot image动作

Qspi

InitQspi();
MoveImage = QspiAccess;
HandoffAddress = LoadBootImage();

SD卡

SD initialization returns file open error or success

  • Status = InitSD(“BOOT.BIN”);
  • MoveImage = SDAccess; 将MoveImage标志置为SDAccess
  • LoadBootImage

u32 InitSD(const char *filename)在sd.c文件中。

u32 InitSD(const char *filename)
{
FRESULT rc;
TCHAR *path = "0:/"; /* Logical drive number is 0 */ 逻辑驱动号为0
/* Register volume work area, initialize device */
rc = f_mount(&fatfs, path, 0); // f_mount在ff.c文件中
fsbl_printf(DEBUG_INFO,"SD: rc= %.8x\n\r", rc);
if (rc != FR_OK) {
return XST_FAILURE;
}
strcpy_rom(buffer, filename);
boot_file = (char *)buffer;
FlashReadBaseAddress = XPAR_PS7_SD_0_S_AXI_BASEADDR;
rc = f_open(&fil, boot_file, FA_READ); // f_open在ff.c文件中
if (rc) {
fsbl_printf(DEBUG_GENERAL,"SD: Unable to open file %s: %d\n", boot_file, rc);
return XST_FAILURE;
}
return XST_SUCCESS;
}

LoadBootImage函数返回的变量为ExecAddress,与该变量有关的函数如下:

/*
* Load execution address of first PS partition
*/
if (PSPartitionFlag && (!ExecAddrFlag)) {
ExecAddrFlag++;
ExecAddress = PartitionExecAddr;
}

而PartitionExecAddr = HeaderPtr->ExecAddr;这里HeaderPtr是从加载镜像的头结构中读出的,如下函数所示:Status = GetPartitionHeaderInfo(ImageStartAddress);

LoadBootImage函数有通过PCAP加载bitstream到PL的过程

Status = PcapLoadPartition((u32*)PartitionStartAddr,
(u32*)PartitionLoadAddr,
PartitionImageLength,
PartitionDataLength,
EncryptedPartitionFlag);

FSBL handoff to valid handoff address or exit in JTAG

FsblHandoff(HandoffAddress);函数通过判断HandoffAddress的值决定调用汇编形式的FsbHandoffJtagExit还是FsbHandoffExit。

FsblHandoff(HandoffAddress); 该函数在当前文件夹下,名为fsbl_handoff.S

该函数主要功能是跳转到HandoffAddress处运行,进入时HandoffAddress需存储在r0中。

Z-Turn-Lite Board Linux开发-FSBL分析_#define