1. 程式人生 > 實用技巧 >zynq7020AMP(LINUX+裸機)核間中斷 SGI

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是裸核,效果槓槓的
轉發註明出處,謝謝