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