1. RBL
在DSP中,芯片厂家都会固化一段初始代码,就是这一段代码可以根据我们bootmode配置的不同来执行不同的启动方式。
根据 Bootloader for KeyStone Architecture User’s Guide (Rev. C)文档14页可知,RBL默认只启动核0,核1到核7默认停止一直等待核0给它们发送IPC(核间中断)之后,才会从Boot Address Register 里读取自己的跳转地址,继续执行任务。
因此想要实现多核启动,首先需要实现核间中断,确认没问题之后再来实现多核启动功能。
2. BootLoader
一般程序会分为BootLoader和APP两部分。
BootLoader执行最基本的初始化操作并将APP搬运执行。
默认RBL只执行核0,因此BootLoader需要将各个核的代码搬运到指定的地址,然后将各个核的跳转地址写入各个核的BOOT_MAGIC_ADDRESS寄存器。下图是部分代码:
#define BOOT_MAGIC_ADDRESS(N) (volatile unsigned int *)(0x1087FFFC + 0x01000000 * N)
#define KICK0 *(volatile unsigned int *)0x02620038
#define KICK1 *(volatile unsigned int *)0x0262003C
#define KICK0_DATA 0x83E70B13
#define KICK1_DATA 0x95A4F1E0
#define BOOTCOMPLETE (volatile unsigned int *)(0x0262013C)
void main()
{
/* 写入指定值解锁寄存器 */
KICK0 = KICK0_DATA;
KICK1 = KICK1_DATA;
/* 核0给核1-7写入程序跳转地址 */
for(i=1; i<8; i++)
*BOOT_MAGIC_ADDRESS(i)=(unsigned int)entryAddr;
*BOOTCOMPLETE = 0xff;
}
上面代码中,KICK0和KICK1是寄存器保护锁,有些寄存器比较重要,随便乱写会导致CPU跑飞,因此需要如上面解锁,重新上锁只需要给这两个寄存器随便写一个值就行了。
核0的BootLoader执行完毕后跳转至自己的程序入口继续工作。
3. APP工程
BootLoader因为只有核0执行,因此不用考虑多核问题。APP工程因为有多核运行,因此需要考虑栈或者变量这些需要分开存放。一般有两种方法:
一:每个核建立一个工程,一共8个核的工程,在cmd文件中就把各个核使用的地址空间分开。烧录时可以将8个核的.bin文件合并为一个.bin文件烧录进去。
二:通过配置MPAX地址映射,使得8个核虽然访问同样的逻辑地址,但是实际上访问的是不同的物理地址。
我个人采用的是第二个方法,第一个方法没用过,太麻烦了。关于MPAX的使用方法参照《spruhj6-KeyStone II Architecture Multicore Shared Memory Controller (MSMC) User Guide》文档。
为了在C语言初始化好堆栈空间之前配置好地址映射,因此需要用汇编写一个MPAX的初始化。并且工程里需要配置入口函数为汇编文件的函数。配置方法如下图:
汇编文件代码如下:
;/*每个核虽然访问的是DDR的逻辑地址一样,
; * 但是通过配置,映射到不同的物理地址,
; * 以实现8个核共用同一个镜像工程
; * core 0: 0xF0000000 -> 0x870000000 -> 0xF0000000 len = 0x1000000
; * core 1: 0xF0000000 -> 0x871000000 -> 0xF1000000
; * core 2: 0xF0000000 -> 0x872000000 -> 0xF2000000
; * core 3: 0xF0000000 -> 0x873000000 -> 0xF3000000
; * core 4: 0xF0000000 -> 0x874000000 -> 0xF4000000
; * core 5: 0xF0000000 -> 0x875000000 -> 0xF5000000
; * core 6: 0xF0000000 -> 0x876000000 -> 0xF6000000
; * core 7: 0xF0000000 -> 0x877000000 -> 0xF7000000
;* */
XMPAXH8 .set 08000044h
XMPAXL8 .set 08000040h
; BADDR=0xF0000000, SEGSZ=16M
MPAXH8VALUE .set 0F0000017h
; RADDR=0x870000000, PERM=0xFF
MPAXL8VALUE .set 0870000FFh
.ref _c_int00
.global MPAX_init
.sect ".btsect"
MPAX_init:
MVKL XMPAXL8,B16
MVKH XMPAXL8,B16
MVC DNUM, B20
AND B20, 7, B20
SHL B20, 24, B20;27-4
MVKL MPAXL8VALUE, B18
MVKH MPAXL8VALUE, B18
MVKL MPAXH8VALUE, B19
MVKH MPAXH8VALUE, B19
ADD B20, B18, B18
STW B19, *+B16[1]
MFENCE
NOP 5
MFENCE
NOP 5
STW B18, *+B16[0]
MFENCE
NOP 5
MFENCE
NOP 5
b _c_int00
nop 5
工程的地址空间分配.cmd文件如下:
-heap 0x1000
-stack 0x2000
MEMORY
{
/* Local L2, 0.5~1MB*/
LL2_RW_DATA: o = 0x00800200 l = 0x0003FE00
/* Shared L2 2~4MB*/
VECTORS: o = 0x0C000000 l = 0x00000200
SL2: o = 0x0C000200 l = 0x001FFE00
/* External DDR3, upto 2GB per core */
DDR3_R_DATA: o = 0x81000000 l = 0x01000000 /*set memory protection attribitue as read only*/
DDR3_RW_DATA: o = 0x82000000 l = 0x06000000 /*set memory protection attribitue as read/write*/
DDR3_CODE: o = 0xF0000000 l = 0x09000000 /*set memory protection attribitue as execution only*/
DDR3_UART_DATA: o = 0xFF000000 l = 0x00FFFFFF
}
SECTIONS
{
.vecs > VECTORS
.text > SL2
.cinit > SL2
.const > SL2
.switch > SL2
// .uart_ws > DDR3_UART_DATA
.uart_ws > SL2
.coresync > SL2
.stack > DDR3_CODE
GROUP
{
.neardata
.rodata
.bss
} > DDR3_CODE
.far > DDR3_CODE
.fardata > DDR3_CODE
.cio > DDR3_CODE
.sysmem > DDR3_CODE
.btsect > SL2
.mysect > SL2
}
汇编初始化地址映射,造成8个核虽然都在访问0xF0000000往后区域的空间,但实际上访问的物理地址是间隔0x1000000字节分开的。只要程序使用的空间不超过0x1000000就不会有问题。不够就自己自定义调大。
最后只需要在APP工程中核0初始化完所有设备后给其他核发送核间中断,即可触发多核启动。可以通过DNUM来判断当前核的编号,需包含<c6x.h>文件。
4. 结束
按上述操作应该可以实现多核启动了。然而多核启动还有多核竞争共享资源、外设问题。还有多核数据CACHE一致性都是老大难问题。任重而道远。