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是裸核,效果杠杠的
转发注明出处,谢谢