取消
显示结果 
显示  仅  | 搜索替代 
您的意思是: 
Highlighted
Observer
Observer
407 次查看
注册日期: ‎09-29-2018

zynq amp(linux+cpu1裸机)模式:linux系统无法单独复位cpu1

跳至解决方案

硬件平台:zynq-7020

vivado版本:2017.04

AMP模式:参考1078,cpu0跑linux系统,cpu1跑裸机程序

工程目标:;linux启动后,通过用户app对cpu1进行reset操作


(1)cpu0 reset cpu1的原理:参考johnmcd在《Reset one of the Zynq APUs》中的回复

在amp模式下:

——>上电启动:会执行bootrom代码,结果是cpu0的linux系统启动后,cpu1会在0xffff fff0处死循环;此时cpu0可以通过向0xffff fff0地址写入cpu1裸机程序在内存中的烧写地址,从而启动cpu1;

——>在系统以启动的情况下,cpu0复位cpu1:此时cpu1的pc指针会跳转到0x0处开始执行代码,而事实上0x0处保存的是fsbl代码;所以fsbl bsp中的boot.s需要处理这种情况!

(2)现在我已经实现的linux+裸机程序的启动,其中:

——>fpga.bit:vicvado 2017.4生成

——>u-boot、dtb、zimage等系统文件:由petalinux自动生成

——>fsbl、cpu1裸机程序:在SDK中生成。fsbl没有使用petalinux而是在SDK中生成的原因是,在SDK中可以方便修改fsbl的源码,并通过SDK在线调试;

这里fsbl和cpu1的bsp源码有区别(特别注意):在设计reset cpu1时,我参考了johnmcd在《Reset one of the Zynq APUs》中的回复和设计文件,如下:

(2.1)fsbl的bsp中的boot.s的内容采用johnmcd修改过的,可实现cpu1 pc指针的跳转:

_boot:
/* Test which processor is running and jump to the catch address */
mrc p15,0,r1,c0,c0,5
and r1, r1, #0xf
cmp r1, #0
bne NotCpu0
ldr r0, =_cpu0_catch
b cpuxCont 
NotCpu0:
cmp r1, #1
bne EndlessLoop0
ldr r0, =_cpu1_catch
b cpuxCont
EndlessLoop0:
wfe
b EndlessLoop0
/* Jump to address pointed to by cpux_catch */
cpuxCont:
ldr lr, [r0]
bx lr
OKToRun:
mrc p15, 0, r0, c0, c0, 0 /* Get the revision */
....

这里有“ldr r0, =_cpu1_catch”,其中cpu1_catch(0x00000024)中保存的是cpu1裸机程序的起始地址,可通过对cpu0的用户程序cpu1_catch(0x00000024)进行赋值,从而跳转到cpu1的裸机程序;

(2.2)cpu1裸机程序的boot.s:

_boot:
#if XPAR_CPU_ID==0
/* only allow cpu0 through */
mrc p15,0,r1,c0,c0,5
and r1, r1, #0xf
cmp r1, #0
beq CheckEFUSE
EndlessLoop0:
wfe
b EndlessLoop0
CheckEFUSE:
ldr r0,=EFUSEStaus
ldr r1,[r0] /* Read eFuse setting */
ands r1,r1,#0x80 /* Check whether device is having single core */
beq OKToRun

/* single core device, reset cpu1 */
ldr r0,=SLCRUnlockReg /* Load SLCR base address base + unlock register */
ldr r1,=SLCRUnlockKey /* set unlock key */
str r1, [r0] /* Unlock SLCR */

ldr r0,=SLCRCPURSTReg
ldr r1,[r0] /* Read CPU Software Reset Control register */
orr r1,r1,#0x22
str r1,[r0] /* Reset CPU1 */

ldr r0,=SLCRlockReg /* Load SLCR base address base + lock register */
ldr r1,=SLCRlockKey /* set lock key */
str r1, [r0] /* lock SLCR */

#elif XPAR_CPU_ID==1
/* only allow cpu1 through */
mrc p15,0,r1,c0,c0,5
and r1, r1, #0xf
cmp r1, #1
beq CheckEFUSE1
b EndlessLoop1

CheckEFUSE1:
ldr r0,=EFUSEStaus
ldr r1,[r0] /* Read eFuse setting */
ands r1,r1,#0x80 /* Check whether device is having single core */
beq OKToRun
EndlessLoop1:
wfe
b EndlessLoop1
#endif

OKToRun:
mrc p15, 0, r0, c0, c0, 0 /* Get the revision */
....

cpu1一旦执行自身bsp中的boot.s,最后会跳转到main函数处;

(2.3)参考TMR 585描述和johnmcd在《Reset one of the Zynq APUs》中的描述,对cpu1复位的主要代码:

#include "xil_misc_psreset_api.h"
#include "xil_io.h"

#define A9_CPU_RST_CTRL (XSLCR_BASEADDR + 0x244)
#define A9_RST1_MASK 0x00000002
#define A9_CLKSTOP1_MASK 0x00000020
#define CPU1_CATCH 0x00000024

#define XSLCR_LOCK_ADDR (XSLCR_BASEADDR + 0x4)
#define XSLCR_LOCK_CODE 0x0000767B

u32 RegVal;
/*
* Setup cpu1 catch address with starting address of app_cpu1. The FSBL initialized the vector table at 0x00000000
* using a boot.S that checks for cpu number and jumps to the address stored at the
* end of the vector table in cpu0_catch and cpu1_catch entries.
* Note: Cache has been disabled at the beginning of main(). Otherwise
* a cache flush would have to be issued after this write
*/
Xil_Out32(CPU1_CATCH, APP_CPU1_ADDR);
/* Unlock the slcr register access lock */
Xil_Out32(XSLCR_UNLOCK_ADDR, XSLCR_UNLOCK_CODE);

// the user must stop the associated clock, de-assert the reset, and then restart the clock. During a
// system or POR reset, hardware automatically takes care of this. Therefore, a CPU cannot run the code
// that applies the software reset to itself. This reset needs to be applied by the other CPU or through
// JTAG or PL. Assuming the user wants to reset CPU1, the user must to set the following fields in the
// slcr.A9_CPU_RST_CTRL (address 0xF8000244) register in the order listed:
// 1. A9_RST1 = 1 to assert reset to CPU0
// 2. A9_CLKSTOP1 = 1 to stop clock to CPU0
// 3. A9_RST1 = 0 to release reset to CPU0
// 4. A9_CLKSTOP1 = 0 to restart clock to CPU0

/* Assert and deassert cpu1 reset and clkstop using above sequence*/
RegVal = Xil_In32(A9_CPU_RST_CTRL);
RegVal |= A9_RST1_MASK;
Xil_Out32(A9_CPU_RST_CTRL, RegVal);
RegVal |= A9_CLKSTOP1_MASK;
Xil_Out32(A9_CPU_RST_CTRL, RegVal);
RegVal &= ~A9_RST1_MASK;
Xil_Out32(A9_CPU_RST_CTRL, RegVal);
RegVal &= ~A9_CLKSTOP1_MASK;
Xil_Out32(A9_CPU_RST_CTRL, RegVal);

/* lock the slcr register access */
Xil_Out32(XSLCR_LOCK_ADDR, XSLCR_LOCK_CODE);

(3)SDK在线调试结果:

在线调试时,cpu0上只运行fsbl,并对main函数修改。cpu1的fbl烧写地址是0x0,cpu1的烧写地址是0x18000000

cpu0:fsbl文件,在main函数中添加(2.3)的复位代码

cpu1:裸机程序,简单的打印hello

测试流程:

debug开始调试,双核全速运行——>cpu1打印消息,cpu0执行复位cpu1代码——>cpu1的pc指针会跳转到0x0处执行,此时单步调试——>cpu1会根据(2.1)中的_cpu1_catch中的地址,跳转到(2.3)的_boot处——>cpu1执行(2.3)中boot.s并跳转到main函数
——>成功复位。

因此,在线调试是没有问题的。

(4)linux系统复位cpu1的问题:

在linux的复位程序中对内存的读写操作是参看petalinux中的poke和peek源码,用户程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>

#define XDDRC_CTRL_BASEADDR 0xF8006000U
#define XSLCR_BASEADDR 0xF8000000U

#define A9_CPU_RST_CTRL (XSLCR_BASEADDR + 0x244)
#define A9_RST1_MASK 0x00000002
#define A9_CLKSTOP1_MASK 0x00000020

/**< SLCR unlock register */
#define XSLCR_UNLOCK_ADDR (XSLCR_BASEADDR + 0x00000008U)
/**< SLCR unlock code */
#define XSLCR_UNLOCK_CODE 0x0000DF0DU

#define XSLCR_LOCK_ADDR (XSLCR_BASEADDR + 0x4)
#define XSLCR_LOCK_CODE 0x0000767B
#define CPU1_CATCH 0x00000024
#define APP_CPU1_ADDR 0x18000000
//=======================================================================//
static int WRITE_OCM_MEM(unsigned int ocm_addr, int val)
{
int fd;
void *ptr;
unsigned addr, page_addr, page_offset;
unsigned page_size=sysconf(_SC_PAGESIZE);

//打开/dev/mem
fd=open("/dev/mem",O_RDWR);
if(fd<1) {
exit(-1);
}
printf("open success \n");
//计算地址
addr=ocm_addr;

page_addr=(addr & ~(page_size-1)); 
page_offset=addr-page_addr;

ptr=mmap(NULL,page_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,(addr & ~(page_size-1)));
if((int)ptr==-1) {
printf("mmap fail!\n");
perror("mmap:");
exit(-1);
}

printf("mmap success\n");

*((unsigned *)(ptr+page_offset))=val;
printf("write success\n");

close(fd);
munmap(ptr, page_size);

return 0; 
}

static int READ_OCM_MEM(unsigned int ocm_addr)
{
int fd;
void *ptr;
unsigned addr, page_addr, page_offset;
unsigned value;
unsigned page_size=sysconf(_SC_PAGESIZE);

fd=open("/dev/mem",O_RDONLY);
if(fd<1) {
exit(-1);
}
addr=ocm_addr;

page_addr=(addr & ~(page_size-1)); 
page_offset=addr-page_addr;

ptr=mmap(NULL,page_size,PROT_READ,MAP_SHARED,fd,(addr & ~(page_size-1)));
if((int)ptr==-1) {
printf("mmap fail!\n");
exit(-1);
}

value = *((unsigned *)(ptr+page_offset)); 
close(fd);
munmap(ptr, page_size);

return value; 
}
//=======================================================================//

int main(int argc, char **argv)
{
//Disable cache on OCM
WRITE_OCM_MEM(0xFFFF0000,0x14de2); // S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0
//Disable cache on fsbl vector table location
WRITE_OCM_MEM(0x00000000,0x14de2); // S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0

printf("cpu0: start\n\r");

unsigned int RegVal;

WRITE_OCM_MEM(CPU1_CATCH,APP_CPU1_ADDR);
WRITE_OCM_MEM(XSLCR_UNLOCK_ADDR,XSLCR_UNLOCK_CODE);

RegVal = READ_OCM_MEM(A9_CPU_RST_CTRL);
RegVal |= A9_RST1_MASK;
WRITE_OCM_MEM(A9_CPU_RST_CTRL,RegVal);
RegVal |= A9_CLKSTOP1_MASK;
WRITE_OCM_MEM(A9_CPU_RST_CTRL,RegVal);
RegVal&= ~A9_RST1_MASK;
WRITE_OCM_MEM(A9_CPU_RST_CTRL,RegVal);
RegVal&= ~A9_CLKSTOP1_MASK;
WRITE_OCM_MEM(A9_CPU_RST_CTRL,RegVal);
WRITE_OCM_MEM(XSLCR_LOCK_ADDR,XSLCR_LOCK_CODE);

printf("cpu0: finish\n\r");
while(1);

return 0;
}

在cpu0的linux系统启动后,执行用户程序,此时:

linux系统会卡死在“WRITE_OCM_MEM(XSLCR_LOCK_ADDR,XSLCR_LOCK_CODE);”处,而cpu1也会死掉

请问能帮忙解答这个问题吗?

0 项奖励
1 解答

已接受的解答
Highlighted
Xilinx Employee
Xilinx Employee
354 次查看
注册日期: ‎05-11-2010

设置mmu table attribute不是在ocm里面写一个值,是要在mmu table里面设置对应page的attribute,你可以参考sdk bsp里的XIL_SetTlbAttributes()或者xapp1078里的MyXil_SetTlbAttributes(),但它们都是在standalone环境下使用的。

另外我记得linux会把address filter的范围扩到0地址开始,就是说0地址是DDR memory,你可以读一下address filter register看一下。

在原帖中查看解决方案

1 回复
Highlighted
Xilinx Employee
Xilinx Employee
355 次查看
注册日期: ‎05-11-2010

设置mmu table attribute不是在ocm里面写一个值,是要在mmu table里面设置对应page的attribute,你可以参考sdk bsp里的XIL_SetTlbAttributes()或者xapp1078里的MyXil_SetTlbAttributes(),但它们都是在standalone环境下使用的。

另外我记得linux会把address filter的范围扩到0地址开始,就是说0地址是DDR memory,你可以读一下address filter register看一下。

在原帖中查看解决方案