(1)参考XAPP1078,修改FSBL,加入DUMMY。
修改FSBL,支持load多个elf,直到遇到标志LOAD地址后,停止load,并返回。之后运行SSBL。
(2)参考UG1186,使用openAMP。
在linux中支持RPMSG,使得linux在CPU0上面启动后,由linux加载CPU1所需要的elf,并由linux启动CPU1。
(3)参考XAPP1079,下载repo,并放置到VIVADO SDK 2017.4中。
新建FSBL时,选择FSBL for AMP。
需要分别建立两个CPU的APP项目,其中CPU1的向导中要注意处理器选项选择CPU1。
CPU1的BSP SETTING需增加-DUSE_AMP=1。该编译选项将影响到CORE1工程代码里中断控制器SCUGIC的初始化函数以及Cache操作函数的编译,若不增加该选项,可能会出现CORE0和CORE1中断异常和Cache一致性维护异常。
每个CPU的APP项目的src目录中按照自己预想的存储器分配方案修改lscript.ld文件中的内容,千万注意不要让两个CPU的DDR地址重合,因为你APP的ELF文件是加载到DDR中执行的。由于没有OS,ELF肯定是加载到每个CPU的DDR起始地址。如果有重合那么一个CPU的ELF会覆盖另一个的。
一旦我们为内核创建了应用程序,我们需要正确的定义DDR内存的映射地址,因为程序将从这些地址开始执行。我们需要编辑链接脚本,如下图所示显示除了 DDR内存的基地址和大小。这一步很重要,如果我们不能正确的为Core 0和 Core 1定义内存地址和大小,在程序执行的过程中就会产生内核间的冲突,出现段地址错误。
(4)自己动手,移植修改SDK的SRC。(推荐!)
直接在standalone_v6_5源码上进行修改。
通过新建一个工程,并打开System.mss,我们可以发现,在SDK2017.4下,OS type只能选择standalone version 6.5.
所以,我们要移植的standalone_amp,也是复制的6.5版本的源码。
另存为standalone_amp_6_5。并在repo中添加。
然后修改其中的某些文件。
我们的依据,是xapp1078的说明文档。
standalone BSP.
Note: The standalone v4.2 BSP was modified as follows:
- armcc/_sys_write.c, gcc/write.c, printc, xil_printf.c: These changes add a new define ‘STDOUT_REDIR’
that prevents the removal of calls to outbyte() if STDOUT_BASEADDRESS is not defined
- armcc/boot.S, gcc/boot.S: These changes remove remapping of virtual address 0x20000000 to physical
address 0x00000000, configures address 0x00000000-0x2fffffff to be unavailable, and disables sharing
and L2 cache on address 0x30000000-0x3fffffff. The startup code was also modified to allow redirecting
cpu0 or cpu1 to a different starting address (This redirecting ability is not used for xapp1078).
- xtime_l.c: The change to this file prevents re-initializing the global timer.
- asm_vectors.S: Two extra vectors are added at the end of the vector table and are used by the boot.S
redirecting code. (This redirecting ability is not used for xapp1078).
先进入SRC的目录。
D:\Xilinx\SDK\2017.4\data\embeddedsw\lib\bsp\standalone_amp_v6_5\src\arm\cortexa9
(a)STDOUT_REDIR 宏定义
在D:\Xilinx\SDK\2017.4\data\embeddedsw\lib\bsp\standalone_amp_v6_5\src\arm\cortexa9\armcc_sys_write.c,
在D:\Xilinx\SDK\2017.4\data\embeddedsw\lib\bsp\standalone_amp_v6_5\src\arm\common\gcc\write.c, 中,
在
D:\Xilinx\SDK\2017.4\data\embeddedsw\lib\bsp\standalone_amp_v6_5\src\common\print.c中,
在
D:\Xilinx\SDK\2017.4\data\embeddedsw\lib\bsp\standalone_amp_v6_5\src\common\xil_printf.c中,
所有涉及到print打印函数的地方,都增加了对应的宏定义,如:
#if defined STDOUT_BASEADDRESS || STDOUT_REDIR
outbyte( *lp++);
#endif
双核通信过程:cpu1会通过xil_printf()函数将打印消息发送给cpu0,cpu0再通过系统uart1将打印消息发送给显示器。
bsp设置中,Select Overview->standalone and change stdin and stdout to None,没有定义输入输出端设备,即没有STDOUT_BASEADDRESS 宏。为了避免xil_printf()无效,才有了STDOUT_REDIR 宏定义。
(我的配置:在vivado中使能uart0:使用EMIO引脚,使能uart1:使用ps侧标准IO;在cpu1的bsp配置中使用uart0;
因此,不需要定义STDOUT_REDIR,demo也可以正常运行)
(b)D:\Xilinx\SDK\2017.4\data\embeddedsw\lib\bsp\standalone_amp_v6_5\src\arm\cortexa9\armcc\boot.S
作用:限定cpu1的裸机程序不可访问低内存空间(保留给linux系统专用)
第一:将虚拟地址0x20000000重新映射到物理地址0x00000000 部分的代码注释(注意:sdk软件自带的standalone不会有这段代码,可忽略此步,这段代码可能是xilinx以前增加的)
第二:将地址0x00000000-0x2fffffff(768M)配置为不可用。注意:这里配置的DDR是1G,因此cpu1运行裸机程序的情况下,配置低768M给cpu0使用,cpu1只是用高256M。如果DDR是512M,分为模式应该是:384M+128M。
第三:设置0x30000000-0x3fffffff仅作为CPU1的内部使用。如果DDR是512M,因该是高128M。
//第一步:注释代码
// /* In case of AMP, map virtual address 0x20000000 to 0x00000000 and mark it as non-cacheable */
//#if USE_AMP==1
// ldr r3, =0x1ff /* 512 entries to cover 512MB DDR */
// ldr r0, =TblBase /* MMU Table address in memory */
// add r0, r0, #0x800 /* Address of entry in MMU table, for 0x20000000 */
// ldr r2, =0x0c02 /* S=b0 TEX=b000 AP=b11, Domain=b0, C=b0, B=b0 */
//mmu_loop:
// str r2, [r0] /* write the entry to MMU table */
// add r0, r0, #0x4 /* next entry in the table */
// add r2, r2, #0x100000 /* next section */
// subs r3, r3, #1
// bge mmu_loop /* loop till 512MB is covered */
//#endif
/* In case of AMP, mark address 0x00000000 - 0x2fffffff DDR as unassigned/reserved */
/* and address 0x30000000 - 0x3fffffff DDR as inner cached only */
#if USE_AMP==1
//第二步:保留低768M(如果DDR是512M,可将768M修改为384M,cpu0也可以正常运行系统)
ldr r3, =0x2ff /* 768 entries to cover 768MB DDR */
ldr r0, =TblBase /* MMU Table address in memory */
ldr r2, =0x0000 /* S=b0 TEX=b000 AP=b00, Domain=b0, C=b0, B=b0 */
mmu_loop:
str r2, [r0] /* write the entry to MMU table */
add r0, r0, #0x4 /* next entry in the table */
add r2, r2, #0x100000 /* next section */
subs r3, r3, #1
bge mmu_loop /* loop till 768MB is covered */
//第三步:设置高256M专为CPU1使用(如果DDR是512M,这里的256M需修改为128M)
ldr r3, =0x0ff /* 256 entries to cover 256MB DDR */
movw r2, #0x4de6 /* S=b0 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b1 */
//注意:CB为01:表示cpu1的MMU设置为了分段地址管理,不支持cache但支持buffer;mmu域是15;保护标志位为0;访问权限为11:可读可写;在ARMv5以上版本处理器中有定义,如果TEX等于1,表示系统支持写时分配cache,这里没有分配cache;s为0:表示此内存不共享!
movt r2, #0x3000 /* S=b0, Section start for address 0x30000000 */
mmu_loop1:
str r2, [r0] /* write the entry to MMU table */
add r0, r0, #0x4 /* next entry in the table */
add r2, r2, #0x100000 /* next section */
subs r3, r3, #1
bge mmu_loop1 /* loop till 256MB is covered */
AMP模式禁用L2 cache(必须)
作用:如果是AMP模式,cpu1就需要禁用L2 cache;(这段代码两个sdk源码均有,不需要用户添加)
/* For AMP, assume running on CPU1. Don't initialize L2 Cache (up to Linux) */
#if USE_AMP!=1
ldr r0,=L2CCCrtl /* Load L2CC base address base + control register */
mov r1, #0 /* force the disable bit */
str r1, [r0] /* disable the L2 Caches */
ldr r0,=L2CCAuxCrtl /* Load L2CC base address base + Aux control register */
ldr r1,[r0] /* read the register */
ldr r2,=L2CCAuxControl /* set the default bits */
orr r1,r1,r2
str r1, [r0] /* store the Aux Control Register */
ldr r0,=L2CCTAGLatReg /* Load L2CC base address base + TAG Latency address */
ldr r1,=L2CCTAGLatency /* set the latencies for the TAG*/
str r1, [r0] /* store the TAG Latency register Register */
ldr r0,=L2CCDataLatReg /* Load L2CC base address base + Data Latency address */
ldr r1,=L2CCDataLatency /* set the latencies for the Data*/
str r1, [r0] /* store the Data Latency register Register */
ldr r0,=L2CCWay /* Load L2CC base address base + way register*/
ldr r2, =0xFFFF
str r2, [r0] /* force invalidate */
ldr r0,=L2CCSync /* need to poll 0x730, PSS_L2CC_CACHE_SYNC_OFFSET */
/* Load L2CC base address base + sync register*/
......
(c)
D:\Xilinx\SDK\2017.4\data\embeddedsw\lib\bsp\standalone_amp_v6_5\src\arm\cortexa9\xtime_l.c修改(必须)
作用:在AMP模式下,global全局定时器肯定会在CPU0的系统中完成初始化,因此避免CPU1启动或是运行过程中CPU1重启时,将global全局定时器reset!
void XTime_SetTime(XTime Xtime)
{
#ifdef USE_AMP
if(Xil_In32(GLOBAL_TMR_BASEADDR + GTIMER_CONTROL_OFFSET) && 0x1) {
//Timer is already enabled so don't reset it
return;
}
#endif
/* Disable Global Timer */
Xil_Out32(GLOBAL_TMR_BASEADDR + GTIMER_CONTROL_OFFSET, 0x0);
/* Updating Global Timer Counter Register */
Xil_Out32(GLOBAL_TMR_BASEADDR + GTIMER_COUNTER_LOWER_OFFSET, (u32)Xtime);
Xil_Out32(GLOBAL_TMR_BASEADDR + GTIMER_COUNTER_UPPER_OFFSET,
(u32)(Xtime>>32));
/* Enable Global Timer */
Xil_Out32(GLOBAL_TMR_BASEADDR + GTIMER_CONTROL_OFFSET, 0x1);
}
注意:
其中用到的宏,名字叫做USE_AMP,而网上很多帖子里,介绍FLAG时,是用的DUSE_AMP,有什么区别,还不清楚。
(d)修改FSBL的模板,修改为FSBL_AMP
D:\Xilinx\SDK\2017.4\data\embeddedsw\lib\sw_apps\zynq_fsbl文件夹,复制为D:\Xilinx\SDK\2017.4\data\embeddedsw\lib\sw_apps\zynq_fsbl_amp。
在新文件夹中,
找到main.c文件打开,在里面添加下面一段代码,用于启动CPU1:
#define sev() __asm__("sev")
#define CPU1STARTADR 0xFFFFFFF0 //这是ZYNQ约定的用来放CPU1的PC的指针地址
#define CPU1STARTMEM 0x20000000 // 这里可以修改为自己的地址
void StartCpu1(void)
{
fsbl_printf(DEBUG_GENERAL,"FSBL: Write the address of the application for CPU 1 to 0xFFFFFFF0\n\r");
Xil_Out32(CPU1STARTADR, CPU1STARTMEM);
dmb(); //waits until write has finished
fsbl_printf(DEBUG_GENERAL,"FSBL: Execute the SEV instruction to cause CPU 1 to wake up and jump to the application\n\r");
sev();
}
再找到Load boot image的位置,将CPU1的启动函数,放置于此位置,改动后的代码段如下:
/*
* Load boot image
*/
HandoffAddress = LoadBootImage();
fsbl_printf(DEBUG_INFO,"Handoff Address: 0x%08lx\r\n",HandoffAddress);
StartCpu1(); /*add starting cpu1*/
修改tcl和mss。
D:\Xilinx\SDK\2017.4\data\embeddedsw\lib\sw_apps\zynq_fsbl_amp\data\zynq_fsbl.tcl
D:\Xilinx\SDK\2017.4\data\embeddedsw\lib\sw_apps\zynq_fsbl_amp\data\zynq_fsbl.mss
先改名为zynq_fsbl_amp.tcl和zynq_fsbl_amp.mss。
然后修改其中的内容,把里面的“standalone”全部修改为“standAMP”。
修改BSP的SRC,将之前修改的AMP版本的BSP包,放在Thirdparty下。
D:\Xilinx\SDK\2017.4\data\embeddedsw\ThirdParty\bsp\standAMP_v6_5\data下,把文件名全部改为standAMP.*。
打开文件,把里面的"standalone"全部改为"standAMP"。
在preference里面,加入我们复制并修改好的repo。