zynq7020AMP(LINUX+裸机)核间中断 SGI

zynq 7020的核间中断SGI

手里有块7020的开发板
想做zynq的核间中断的原始驱动力是最开始做amp的测试(一个核跑linux +一个核跑裸机)
关于amp的实现方式赛灵思提供了
ug1186即openamp
xapp1087两种方式,这两个文档在赛灵思的官网都可以下到
从版本管理的角度来说,个人认为openamp的框架要好于xapp1087提供的方式,将裸核的固件作为一个特殊的应用版本管起来就行了,并且remoteproc和rpmsg都是linux的成熟框架,事实用起来也没什么问题

看到xapp1078后,由此想到,自己实现一个通过ocm或者ddr一片特殊区域实现一个自己的核间通信,即两核通过共享内存交换数据,通过sgi软中断来通知对方,这样一来可以作为rpmsg的方案备选,二来通信的数据量什么的都可控一些

想起来比较容易,也比较清晰,但是做起来,想查查资料,发现少的可怜,

主要的难点在于核间中断,内存的读写,没什么说的,主要用到是sgi

如果双核都各自跑裸机程序,实际上也变得简单了,问题是,我是一个核跑linux,一个核跑裸机,神烦
最后把gic的代码撸了撸,没撸太明白,但是不幸中的万幸,尝试了尝试,实现了,过程不多说,直接上结果
sgi一共支持最多16个,这里把0给了cpu0,1给了裸核
linux这端需要加的代码

static  void  icc_kick(void)

{

    printk("icc interrupt \r\n");

    printk("!!!!!!!!!!!!!!!!!!!!\r\n");

}

ssize_t  irq_store(struct device *dev, struct device_attribute *attr,

const  char  *buf, size_t  count) {

u8 irqnum =  buf[0] -  '0';

    if(irqnum >=  16)

        dev_err(dev, "Invalid soft IRQ num %u\n", irqnum);

    else

        gic_raise_softirq(cpumask_of(1), irqnum);

    return count;

}

static  DEVICE_ATTR_WO(irq);
//我为了省事就直接加在remoteproc的节点下了,如果有需要就自己写个节点往里一加就行了,
//这块就是生成个irq的节点,不太明白的看看sys文件系统,就行了
static  int  zynq_remoteproc_probe(struct platform_device *pdev)

{
....
ret =  set_ipi_handler(0, icc_kick,

            "icc kick");//可以从设备树获取

ret =  device_create_file(&local->rproc->dev, &dev_attr_irq);

    if (ret) {

        dev_err(&pdev->dev, "device_create_file %s %d\n",

        dev_attr_irq.attr.name, ret);

        goto attr_up_err;

    }
attr_up_err:

    device_remove_file(&local->rproc->dev, &dev_attr_irq)
....
}

裸机这边icc.c

/*

* Copyright (c) 2014, Mentor Graphics Corporation

* All rights reserved.

*

* Copyright (C) 2017 Xilinx, Inc. All rights reserved.

*

* Redistribution and use in source and binary forms, with or without

* modification, are permitted provided that the following conditions are met:

*

* 1\. Redistributions of source code must retain the above copyright notice,

* this list of conditions and the following disclaimer.

* 2\. Redistributions in binary form must reproduce the above copyright notice,

* this list of conditions and the following disclaimer in the documentation

* and/or other materials provided with the distribution.

* 3\. Neither the name of Mentor Graphics Corporation nor the names of its

* contributors may be used to endorse or promote products derived from this

* software without specific prior written permission.

*

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"

* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE

* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE

* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE

* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR

* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF

* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS

* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN

* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)

* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE

* POSSIBILITY OF SUCH DAMAGE.

*/

/**************************************************************************************

* This is a sample demonstration application that showcases usage of rpmsg

* This application is meant to run on the remote CPU running bare-metal code.

* It echoes back data that was sent to it by the master core.

*

* The application calls init_system which defines a shared memory region in

* MPU settings for the communication between master and remote using

* zynqMP_r5_map_mem_region API,it also initializes interrupt controller

* GIC and register the interrupt service routine for IPI using

* zynqMP_r5_gic_initialize API.

*

* Echo test calls the remoteproc_resource_init API to create the

* virtio/RPMsg devices required for IPC with the master context.

* Invocation of this API causes remoteproc on the bare-metal to use the

* rpmsg name service announcement feature to advertise the rpmsg channels

* served by the application.

*

* The master receives the advertisement messages and performs the following tasks:

*   1\. Invokes the channel created callback registered by the master application

*   2\. Responds to remote context with a name service acknowledgement message

* After the acknowledgement is received from master, remoteproc on the bare-metal

* invokes the RPMsg channel-created callback registered by the remote application.

* The RPMsg channel is established at this point. All RPMsg APIs can be used subsequently

* on both sides for run time communications between the master and remote software contexts.

*

* Upon running the master application to send data to remote core, master will

* generate the payload and send to remote (bare-metal) by informing the bare-metal with

* an IPI, the remote will send the data back by master and master will perform a check

* whether the same data is received. Once the application is ran and task by the

* bare-metal application is done, master needs to properly shut down the remote

* processor

*

* To shut down the remote processor, the following steps are performed:

*   1\. The master application sends an application-specific shut-down message

*    to the remote context

*   2\. This bare-metal application cleans up application resources,

*    sends a shut-down acknowledge to master, and invokes remoteproc_resource_deinit

*    API to de-initialize remoteproc on the bare-metal side.

*   3\. On receiving the shut-down acknowledge message, the master application invokes

*    the remoteproc_shutdown API to shut down the remote processor and de-initialize

*    remoteproc using remoteproc_deinit on its side.

*

**************************************************************************************/

#include  "xil_printf.h"

#include  "openamp/open_amp.h"

#include  "rsc_table.h"

#include  "icc.h"

#define  LPRINTF(format, ...) xil_printf(format, ##__VA_ARGS__)

#define  LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__)

#define  INTC_DEVICE_ID   XPAR_PS7_SCUGIC_0_DEVICE_ID

XScuGic InterruptController; /* Instance of the Interrupt Controller */

u32 SGI_INTR;

int SGI_trigered;

void  SGI0_INTR_ID_ISR (void);

#define  DELAY  50000000

#define  COMM_VAL (*(volatile unsigned long *)(0xFFFF8000))

#define  COMM_TX_FLAG (*(volatile unsigned long *)(0xFFFF9000))

#define  COMM_TX_DATA (*(volatile unsigned long *)(0xFFFF9004))

#define  COMM_RX_FLAG (*(volatile unsigned long *)(0xFFFF9008))

#define  COMM_RX_DATA (*(volatile unsigned long *)(0xFFFF900C))

int  uart_init(void)

{

    u32 RegValue;

    /* ps7_clock_init_data_3_0 */

RegValue =  Xil_In32(0xF8000008);

RegValue &=  ~0x0000FFFF;

RegValue |=  0x0000DF0D;

    Xil_Out32(0xF8000008, RegValue);

    /* Only Enable Uart Reference Clock */

RegValue =  Xil_In32(0xF8000154);

RegValue &=  ~0x00003F33;

RegValue |=  0x00000A03;

    Xil_Out32(0xF8000154, RegValue);

RegValue =  Xil_In32(0xF800012C);

RegValue &=  ~0x01FFCCCD;

RegValue |=  0x017D444D;

    Xil_Out32(0xF800012C, RegValue);

RegValue =  Xil_In32(0xF8000004);

RegValue &=  ~0x0000FFFF;

RegValue |=  0x0000767B;

    Xil_Out32(0xF8000004, RegValue);

}

u32 SetupSGIIntrSystem(XScuGic *IntcInstancePtr,Xil_InterruptHandler Handler, u32 DeveiceId, u32 SgiIntr, u32 CpuNo)

{

    int Status;

XScuGic_Config *IntcConfig;

IntcConfig =  XScuGic_LookupConfig(DeveiceId);

    if (NULL  == IntcConfig)

    {

        return XST_FAILURE;

    }

Status =  XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,IntcConfig->CpuBaseAddress);

    if (Status != XST_SUCCESS)

    {

        return XST_FAILURE;

    }

    XScuGic_SetPriorityTriggerType(IntcInstancePtr, SgiIntr,0xd0, 0x3);

Status =  XScuGic_Connect(IntcInstancePtr, SgiIntr, (Xil_ExceptionHandler)Handler,0);

    if (Status != XST_SUCCESS)

    {

        return XST_FAILURE;

    }

    XScuGic_Enable(IntcInstancePtr, SgiIntr);

    XScuGic_InterruptMaptoCpu(IntcInstancePtr,CpuNo,SgiIntr);

    return XST_SUCCESS;

}

void  ExceptionSetup(XScuGic *IntcInstancePtr)

{

    /*

     * Initialize the exception table

     */

    Xil_ExceptionInit();

    /*

     * Register the interrupt controller handler with the exception table

     */

    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,

             (Xil_ExceptionHandler)XScuGic_InterruptHandler,

             IntcInstancePtr);

    /*

     * Enable non-critical exceptions

     */

    Xil_ExceptionEnable();

}

void  SGI1_INTR_ID_ISR (void)

{

 LPRINTF("CPU0: The software interrupt0 has been triggered\n\r");

     SGI_trigered=1;

}

/*-----------------------------------------------------------------------------*

* Application entry point

*-----------------------------------------------------------------------------*/

int  main(void)

{

    int Status;

    SGI_INTR=SGI1_INTR_ID;

    SGI_trigered=0;

    uart_init();

Status =  SetupSGIIntrSystem(&InterruptController,(Xil_ExceptionHandler)SGI1_INTR_ID_ISR,

            INTC_DEVICE_ID, SGI_INTR,CPU_NO1);

    if (Status != XST_SUCCESS)

    {

        return XST_FAILURE;

    }

    ExceptionSetup(&InterruptController);

    LPRINTF("Starting application...\r\n");

    /* Suspend processor execution */

    while(1)

    {

        while(SGI_trigered==0)

        {

            LPRINTF("CPU1 runing...\n\r");

            sleep(2);

        }

        SGI_trigered=0;

        for(int i=0;i<DELAY;i++);

        LPRINTF("CPU0: Trigger interrupt1 to CPU1\n\r");

        for(int i=0;i<DELAY;i++);

Status =  XScuGic_SoftwareIntr(&InterruptController,SGI0_INTR_ID,0x1<<CPU_NO0);

        if (Status != XST_SUCCESS)

        {

            return XST_FAILURE;

        }

    }

    return Status;

}

icc.h

#include  "xscugic.h"

#include  "xil_io.h"

#ifndef  SRC_ZYNQ_SGI_H_

#define  SRC_ZYNQ_SGI_H_

/*Register ICDSGIR setting*/

#define  SGI0_INTR_ID  0

#define  SGI1_INTR_ID  1

#define  SGI2_INTR_ID  2

#define  SGI3_INTR_ID  3

#define  SGI4_INTR_ID  4

#define  SGI5_INTR_ID  5

#define  SGI6_INTR_ID  6

#define  SGI7_INTR_ID  7

#define  SGI8_INTR_ID  8

#define  SGI9_INTR_ID  9

#define  SGI10_INTR_ID  10

#define  SGI11_INTR_ID  11

#define  SGI12_INTR_ID  12

#define  SGI13_INTR_ID  13

#define  SGI14_INTR_ID  14

#define  SGI15_INTR_ID  15

/*TargetListFilter bits[25:24]

* 0b00: send the interrupt to the CPU interfaces

specified in the CPUTargetList field

0b01: send the interrupt to all CPU interfaces

except the CPU interface that requested the

interrupt

0b10: send the interrupt on only to the CPU

interface that requested the interrupt

0b11: reserved*/

#define  TRIGGER_SELECTED  0x00000000

#define  TRIGGER_SELF  0x02000000

#define  TRIGGER_OTHER  0x01000000

/*CPUTargetList bits[23:16]

When TargetListFilter is 0b00, defines the CPU

interfaces the Distributor must send the interrupt

to.

Each bit refers to the corresponding CPU

interface.*/

#define  CPU_NO0  0

#define  CPU_NO1  1

#define  CPU_ID_LIST  0x00010000

/*ICDSGIR SGI触发寄存器地址*/

#define  ICDSGIR  0xF8F01F00

#define  ICDIPTR  0xF8F01800

/* SGI中断初始化函数

* 该函数初始化指定的SGI中断到本CPU上并绑定对应的ISR

* 参数说明:

* XScuGic *IntcInstancePtr 中断控制器实例

* Xil_InterruptHandler Handler ISR函数名

* u32 INTC_DEVICE_ID 中断控制器设备号

* u32 SGI_INTR SGI中断号

* CPU_NO 运行当前程序的CPU号

*/

u32 SetupSGIIntrSystem(XScuGic *IntcInstancePtr,Xil_InterruptHandler Handler,

u32 INTC_DEVICE_ID,u32 SGI_INTR,u32 CPU_NO);

/* Exception初始化函数

* 参数说明:

* XScuGic *IntcInstancePtr 中断控制器实例

*/

void  ExceptionSetup(XScuGic *IntcInstancePtr);

/* SGI触发函数

* 该函数触发指定的SGI到指定的CPU上

* 参数说明:

* u32 CPUNO 目标CPU号

* u32 INTR SGI中断号

* u32 TargetSelect 选择过滤,该参数决定了CPUNO是否有效

* TRIGGER_SELECTED 0x00000000 根据CPUNO选择触发的CPU

* TRIGGER_SELF 0x02000000 触发自身,此时CPUNO无效

* TRIGGER_OTHER 0x01000000 触发除了自身的其他CPU,此时CPUNO无效

*/

void  SGITriggerTargetCPU(u32 CPUNO,u32 INTR,u32 TargetSelect);

/* 设定中断触发的目标CPU

* 参数说明:

* u32 INTR 中断号

* u32 CPUID 目标CPU的ID号

*/

void  SetINTRTargetCPU(u32 INTR,u32 CPUID);

#endif  /* SRC_ZYNQ_SGI_H_ */

上机测一下
左侧uart是linux右侧uart是裸核,效果杠杠的

转发注明出处,谢谢

posted @ 2020-07-27 10:32  tccxy  阅读(3168)  评论(1编辑  收藏  举报