在实际项目中,为了降低硬件成本,一般会选择将程序固化在flash中,并在系统启动时动态搬运程序到DDR中并运行,这就需要引导程序的步骤,xilinx SDK 2018.3 应用模板中有srec bootloader工程,此工程只要配置好flash中应用程序的偏移地址,即可实现程序搬运并正常运行。
在编译完成嵌入式应用软件程序生成elf可执行文件之后,要向程序从flash启动,我们需要将elf可执行文件通过Xilinx 2018.3在的Program Flash小软件将应用程序写入到qspi Flash中。
根据官方的指导文件,需要我们勾选convert ELF to bootloadable SREC format and program, 将将ELF应用程序转换为能够boot的SREC格式文件,
根据上述设计,引导文件会搬运srec格式文件,将其放置在ddr中并运行,但是在实践中,此搬运过程非常耗时,为了解决这一问题,尝试直接加载elf文件,而不是srec文件。
分析srec bootloader工程中bootloader.c 源文件,发现引导程序循环读取flash中的srec格式数据,然后对数据进行转码,继而拷贝到DDR中,此过程非常消耗资源,费时费力。
static uint8_t load_exec ()
{
uint8_t ret;
void (*laddr)();
int8_t done = 0;
srinfo.sr_data = sr_data_buf;
while (!done) {
if ((ret = flash_get_srec_line (sr_buf)) != 0)
return ret;
if ((ret = decode_srec_line (sr_buf, &srinfo)) != 0)
return ret;
#ifdef VERBOSE
display_progress (srec_line);
#endif
switch (srinfo.type) {
case SREC_TYPE_0:
break;
case SREC_TYPE_1:
case SREC_TYPE_2:
case SREC_TYPE_3:
memcpy ((void*)srinfo.addr, (void*)srinfo.sr_data, srinfo.dlen);
break;
case SREC_TYPE_5:
break;
case SREC_TYPE_7:
case SREC_TYPE_8:
case SREC_TYPE_9:
laddr = (void (*)())srinfo.addr;
done = 1;
ret = 0;
break;
}
}
#ifdef VERBOSE
print ("\r\nExecuting program starting at address: ");
putnum ((uint32_t)laddr);
print ("\r\n");
#endif
(*laddr)();
/* We will be dead at this point */
return 0;
}
为了缩短系统启动时间,可以精简转码过程。可以直接搬运elf格式文件而不是srec格式文件,即可实现此步骤。
有关elf文件的相关介绍如下:
https://blog.csdn.net/mergerly/article/details/94585901
画布多说,elf搬运源码如下:
typedef struct
{
unsigned char ident[EI_NIDENT]; /* Magic number and other info */
Elf32_Half type; /* Object file type */
Elf32_Half machine; /* Architecture */
Elf32_Word version; /* Object file version */
Elf32_Addr entry; /* Entry point virtual address */
Elf32_Off phoff; /* Program header table file offset */
Elf32_Off shoff; /* Section header table file offset */
Elf32_Word flags; /* Processor-specific flags */
Elf32_Half ehsize; /* ELF header size in bytes */
Elf32_Half phentsize; /* Program header table entry size */
Elf32_Half phnum; /* Program header table entry count */
Elf32_Half shentsize; /* Section header table entry size */
Elf32_Half shnum; /* Section header table entry count */
Elf32_Half shstrndx; /* Section header string table index */
} Elf32_Ehdr;
/* Program segment header. */
typedef struct
{
Elf32_Word type; /* Segment type */
Elf32_Off offset; /* Segment file offset Segment鐎电懓绨查惃鍕敶鐎圭懓婀弬鍥︽閻ㄥ嫬浜哥粔?*/
Elf32_Addr vaddr; /* Segment virtual address Segment閸︺劌鍞寸�涙ü鑵戦惃鍕殠閹冩勾閸�?*/
Elf32_Addr paddr; /* Segment physical address */
Elf32_Word filesz; /* Segment size in file */
Elf32_Word memsz; /* Segment size in memory */
Elf32_Word flags; /* Segment flags */
Elf32_Word align; /* Segment alignment */
} Elf32_Phdr;
#define PT_NULL 0 /* Program header table entry unused */
#define PT_LOAD 1 /* Loadable program segment */
#define PT_DYNAMIC 2 /* Dynamic linking information */
#define PT_INTERP 3 /* Program interpreter */
#define PT_NOTE 4 /* Auxiliary information */
#define PT_SHLIB 5 /* Reserved */
#define PT_PHDR 6 /* Entry for header table itself */
#define PT_TLS 7 /* Thread-local storage segment */
#define PT_NUM 8 /* Number of defined types */
Elf32_Ehdr elf;
Elf32_Phdr phdr;
static uint8_t load_exec ()
{
int i = 0;
int ret = 0;
int addr = 0;
char elfStartOffset = 0;
u8 readBuffer[256] = {0};
void (*laddr)();
print(" start readData from flash!!!\n");
memset(readBuffer, 0x00, sizeof(readBuffer));
ret = ReadData(&Isf, 0xf00000, readBuffer, 64);
if(ret != XST_SUCCESS)
{
print("readData from flash failed\n");
return -1;
}
for(i = 0; i < 64; i++)
{
if((readBuffer[i] == 0x7f) && (readBuffer[i + 1] == 'E') && (readBuffer[i + 2] == 'L') && (readBuffer[i + 3] == 'F') )
{
elfStartOffset = i;
//printf(" elfStartOffset = %d\n", elfStartOffset);
break;
}
}
memcpy((void *)elf.ident, (void *)&readBuffer[elfStartOffset], sizeof(Elf32_Ehdr));
// printf("\n-------------------\n");
// printf("elf.type = %d\r\n", elf.type);
// printf("elf.machine = %d\r\n", elf.machine);
// printf("elf.version = %d\r\n", elf.version);
// printf("elf.entry = %d\r\n", elf.entry);
// printf("elf.phoff = %d\r\n", elf.phoff);
// printf("elf.shoff = %d\r\n", elf.shoff);
// printf("elf.flags = %d\r\n", elf.flags);
// printf("elf.ehsize = %d\r\n", elf.ehsize);
// printf("elf.phentsize = %d\r\n", elf.phentsize);
// printf("elf.phnum = %d\r\n", elf.phnum);
// printf("elf.shentsize = %d\r\n", elf.shentsize);
// printf("elf.shnum = %d\r\n", elf.shnum);
// printf("elf.shstrndx = %d\r\n", elf.shstrndx);
// printf("\n-------------------\n");
/* move to ddr */
for(i = 0; i < elf.phnum; i++)
{
memset(readBuffer, 0x00, sizeof(readBuffer));
ret = ReadData(&Isf, 0xF00000 + elf.phoff + i * sizeof(Elf32_Phdr), readBuffer, sizeof(Elf32_Phdr) + SPI_VALID_DATA_OFFSET);
if(ret == XST_SUCCESS)
{
memcpy((void *)&phdr, (void *)&readBuffer[elfStartOffset], sizeof(Elf32_Phdr));
// printf("phdr.type = %d\n", phdr.type);
// printf("phdr.offset = %d\n", phdr.offset);
// printf("phdr.vaddr = %08x\n", phdr.vaddr);
// printf("phdr.paddr = %08x\n", phdr.paddr);
// printf("phdr.filesz = %d\n", phdr.filesz);
// printf("phdr.memsz = %d\n", phdr.memsz);
// printf("phdr.flags = %d\n", phdr.flags);
// printf("phdr.align = %d\n", phdr.align);
// printf("-------------------\n");
if(phdr.type == PT_LOAD)
{
for(addr = 0; addr < phdr.filesz; addr += EFFECTIVE_READ_BUFFER_SIZE)
{
feedWdg();
if((addr + EFFECTIVE_READ_BUFFER_SIZE) > phdr.filesz)
{
memset(readBuffer, 0x00, sizeof(readBuffer));
ret = ReadData(&Isf, 0xF00000 + phdr.offset + addr, readBuffer, phdr.filesz - addr + SPI_VALID_DATA_OFFSET);
}
else
{
memset(readBuffer, 0x00, sizeof(readBuffer));
ret = ReadData(&Isf, 0xF00000 + phdr.offset + addr, readBuffer, EFFECTIVE_READ_BUFFER_SIZE + SPI_VALID_DATA_OFFSET);
}
if(ret == XST_SUCCESS)
{
if((addr + EFFECTIVE_READ_BUFFER_SIZE) > phdr.filesz)
{
memcpy((void * )(phdr.paddr + addr), (void *)&readBuffer[elfStartOffset], phdr.filesz - addr);
}
else
{
memcpy(phdr.paddr + addr, (void *)&readBuffer[elfStartOffset], EFFECTIVE_READ_BUFFER_SIZE);
}
}
else
{
print("Failed to read ELF program segment");
return -1;
}
}
}
if(phdr.memsz > phdr.filesz)
{
memset((void *)(phdr.paddr + phdr.filesz), 0, phdr.memsz - phdr.filesz);
}
}
else
{
print("Failed to read ELF program header");
return -1;
}
}
laddr = (void (*)())elf.entry;
(*laddr)();
return 0;
}
经过上述修改,再次启动系统,程序能能够迅速加载并运行。