本帖最后由 freebsder 于 2015-3-8 02:32 编辑
freebsder发于
www.eeworld.com.cn,如需转载或转发请保留此行声明。
板子还没有拿到,以下文字是基于人肉计算机虚拟跑出来的,所以很可能会在板子拿到之后进行修正。
本来只想看看lpc54xxx的低功耗相关特性,不过既然有两个核也顺便瞄了一下这个功能,
没想到的是居然nxp这次还搞了保留。。。有朋友也在询问这部分东西,所以一边墙裂鄙视这种流行于山寨国的行为,一边花了接近两个小时来寻找答案,先整理出来供大家参考。
新建基于lpcxpresso的S/M(艹,好工程,Slave/Master)的工程过程就不详表了,新建一个合一的image。
一、代码
里面有几个宏__MULTICORE_MASTER,__MULTICORE_MASTER_SLAVE_M4SLAVE, __MULTICORE_MASTER_SLAVE_M0SLAVE开关,需要注意的是虽然有定义__MULTICORE_MASTER_SLAVE_M4SLAVE这样的开关,但是不意味着M4核在启动时可以作为S,这里的定义只为了保持索引的完整性和对未来的扩展(假设下一款lpc65xxx就可以呢)。
M中的cr_startup_lpc5410x.c在ResetISR中的一段汇编,后面有调用ResetISR2。ResetISR2才是一般意义上cortex的ResetISR,前面的汇编是一段stub来辅助引导。在M的boot_multicore_slave.c中的boot_multicore_slave调用Chip_CPU_CM0Boot启动S。结果nxp以power库的形式给出来的,墙裂鄙视之,后面第三步在看回头说。S的ResetISR就比较简单了,基本就是cp data和bss清0,然后调用它自己的main,中间systeminit都被宏关掉了,想来所有资源(除了几个IRQ之外)都是可共享的,S也不需要来抢M的活。
二、工程
lpcxpresso是基于makefile的过程管理和gcc/ld的编译链接,这里也只涉及和双核有关的叙述,如果不熟悉的话,我也没法了。
S的ldscript是一个常规链接脚本,技巧性的东西在M的ldscript,S/M的Makefile中。
M的ldscript中.data_RAM2段完全包含S的所有代码,看见里面其实填充的是*(.core_m0slave)。另外__start_data_RAM2,__core_m0slave_END__等符号,在M的引导中需要为S设置,这是下面“引导”的前奏。
M的ld过程中,可以看到会把S.axf.o(S是Slave的工程名称)作为链接对象之一,添加到M.axf(M是Master的工程名称)。
倒回来看S的makefile,看到nxp又把关键部分写在很长的一行,不拉滚动还看不到的一句 arm-none-eabi-objcopy 。。。
里面关键的是 --redefine-sym __vectors_start__=__vectors_start___core_m0slave --keep-symbol __vectors_start___core_m0slave --rename-section .text=".core_m0slave" --rename-section .data=".core_m0slave.data"。
好吧,到这里也应该能知道M的ldscript中填充的东西,也应该能知道M的ld过程就是把S.axf.o的.data和.text两个段端到M的.data_RAM2中去。
三、引导
再一次鄙视nxp的这种行为,弄个库来糊弄人。
上电或复位的时候,两个核同时运行,但是S马上进入sleep状态,只有M继续按照正常流程运行。两者只有在引导过程中分S和M,一旦两者都引导起来之后,从执行上看他们地位平等,就没有S和M之分了。
M中的那一段汇编stub,它确实是汇编。这段汇编默认作为S/M复位时都要执行的一小段代码。S的复位,也是按照cortex手册规定的地址,当然这时候就和M的复位异常是一个地方。所以这段确实是汇编的代码只能用汇编来做是为了确保编出来的东西M0+核与M4核都可以运行。需要注意的是这段代码也和上面说的一些宏一样,为了完整性和扩展性,也把M做了S化处理。
那么里面有三个寄存器0x40000300,0x40000304,0x40000308手册里没有涉及。0x40000304 存放boot执行地址,0x40000308存放堆栈栈顶。这里相当于M作为S的固件,为M0+核填充了interrupt vector[0]和[1]。0x40000300 是控制寄存器,复位初始4D。[31:7]是保留位;[6:6]用于设置SM哪一个可以作为控制CPU来使得系统进入Sleep,Deep Sleep,Power-down和Deep Power-down模式,0=M0+,1=M4;[5:5]复位M0+核,0=disable,1=enable复位; [4:4]复位M4核, 0=disable,1=enable复位; [3:3] M0+时钟使能,0=disable,1=enable; [2:2] M4时钟使能,0=disable,1=enable; [1:1]保留位; [0:0]设置哪一个核作为M处理器,0=M0+核,1=M4核。这里实在没看出来不开放这部分内容的意义啊,好吧,把fsl都合并了,太有钱了,任性点也能理解。
再看那段小汇编,这里要注意的是S和M在复位时执行的是同一段代码,然后这段代码为了完整性写的比较绕。里面有注释,很好理解,大概意思是找CPUID寄存器先确定当前是哪个核,然后分开引导。如果M0+执行的时候发现0x40000304不为0(这是在M为S初始化环境时设置进去的),那么再把0x40000308拿出来,设置成SP,然后跳过去;如果M4执行的时候,基本可以忽略这段汇编而直接跳进ResetISR2。
从M的填充代码里可以看到相当于还是取出了S的int vector[0]和[1],标准的cortex中断向量,
volatile unsigned int resetaddr;
volatile unsigned int spaddr;
spaddr = *slavevectortable_ptr;
resetaddr = *(slavevectortable_ptr+1);
Chip_CPU_CM0Boot((uint32_t *)resetaddr, (uint32_t *)spaddr);
Chip_CPU_CM0Boot比较简单,可以找这段汇编
00000000
:
0: f04f 4380 mov.w r3, #1073741824 ; 0x40000000
4: 4a07 ldr r2, [pc, #28] ; (24 )
6: f8c3 1308 str.w r1, [r3, #776] ; 0x308
a: f8c3 0304 str.w r0, [r3, #772] ; 0x304
e: f8d3 0300 ldr.w r0, [r3, #768] ; 0x300
12: 4905 ldr r1, [pc, #20] ; (28 )
14: 4302 orrs r2, r0
16: 4301 orrs r1, r0
18: f8c3 1300 str.w r1, [r3, #768] ; 0x300
1c: f8c3 2300 str.w r2, [r3, #768] ; 0x300
20: 4770 bx lr
22: bf00 nop
24: c0c40008 sbcgt r0, r4, r8
28: c0c40028 sbcgt r0, r4, r8, lsr #32
简单说,就是把引导地址和堆栈指针放到0x40000304和0x40000308里面去,然后把ctrl(0x40000300)的[5:5]复一次位(18:xxx),就可以正常启动了(1c:xxx)。
好了,引导完了。